C语言基础-01-指针

来源:互联网 发布:雷蛇淘宝假的 编辑:程序博客网 时间:2024/06/03 12:07

一.指针的概念

指针是编程语言中的一个对象,利用地址,它的值直接指向存在内存中某一的值。由于通过地址能找到所需变量的存储地址,可以说地址指向它所对应的变量单元。因此,将地址被形象化的称为“指针”。意思是通过它能找到以它为地址的内存单元
指针变量也是一个变量,指针存放的内容是一个地址,该地址指向一块内存空间

这里写图片描述

二.指针变量

指针变量是指针类型的数据,指针类型和基本类型一样,是C语言常用的数据类型之一。

2.1 指针变量的定义:

定义指针变量时要明确指针指向的数据的类型,因为不同类型的数据在内存中的长度是不同的,如:一般的计算机中int型占4个字节,char类型占1个字节,指针要明确自己指向的数据在内存中的长度,这样才能正确的对指向的数据进行操作

int *p; //表示定义一个指针变量*p;     //代表指针所指内存的数据

指针变量只能存放地址,不能将一个int型变量直接赋值给一个指针,如,int *p = 100是错误的

2.2 指针类型的兼容性

指针变量之间赋值比普通数据类型赋值要求更为严格,例如:不可以把double *赋值给int *
相同类型的指针指向相同类型的变量地址,不能用一种类型的指针指向与其不同类型变量的地址

2.3 指针变量常用的运算符:

取地址运算符:&

取地址运算符“&”,对变量(基本类型数据)使用该运算符,可以获得变量在内存当中的地址(一个指针类型的数据)

int p1 = 1;     //定义一个int类型的变量p1int *p = &p1;   //将p1的地址赋给p

解引用运算符:*

解引用运算符“*”,对地址(指针类型数据)使用该运算符,可以获得地址对应的内存单元中存储的数据

int p1 = 1;     //定义一个int类型的变量p1int *p = &p1;   //将p1的地址赋给pprintf("p1的值为%d",*p);   //输出指针p指向的变量中存储的数据

三.指针的分类

3.1 无类型指针

定义一个指针变量,但不指定它指向哪种具体的数据类型,这是这个指针就是无类型指针

void *p; //使用void关键字定义了一个无类型指针

可以通过强制转化将void *无类型指针转换为其他类型的指针,也可以用(void *)强制转换将其他类型的指针强制转换为void无类型指针
void无类型指针转换为其他类型指针:

void *p;    //定义一个无类型指针(int*)p;    //使用时将p强制转换为int类型的指针

其他类型指针转换为void无类型指针:

int *p;     //定义一个int类型指针(void*)p;   //使用时将p强制转换为void无类型的指针

3.2 空指针

指向NULL的指针叫空指针
空指针通常在给指针初始化时使用,让声明的指针指向NULL,当使用时再给指针赋准确的值

C语言类型:NULL

NULL在C语言中的定义为( void * ) 0

3.3 野指针

没有具体指向任何变量地址的指针叫野指针
野指针是没有明确指向的指针,比如声明一个指针却没有对其初始化int * p,此时该指针的指向不明确,可能会指向系统占用的内存或其他垃圾数据,编程时应注意避免野指针的出现

3.4 指向常量的指针与指针常量

声明指针变量时使用const关键字

指向常量的指针:

const char *p;  //定义一个指向常量的指针,该指针可以指向不同的常量

指针常量:

char *const p;  //定义一个指针常量,一旦初始化之后其内容不可改变,即该指针指向一个固定的地址,该地址中的内容时可以变化的

3.5 数组指针

数组名是一个指针,即指向数组首元素的地址
若定义一个int类型的数组a[10],则a是一个int类型的指针

int a[10];int *p = a;

“[]”也是一个运算符,其功能和“*”解引用运算符的功能相同,都可取出地址中存放的数据

3.4 多级指针

指针就是一个变量,既然是变量就也存在内存地址,所以可以定义一个指向指针的指针,即指向指针的指针(二级指针)

int i = 10;int *p1 = &i;int **p2 = &p1;printf("%d\n", **p2);   //输出一个地址

这里写图片描述

指向多维数组的指针

表达式 含义 int a[3][5] 定义一个二维数组,a为数组首地址 int (*a)[5] 定义一个指向int [5]类型的指针变量a a[0], *(a + 0), *a 0行,0列元素地址 a + 1 第1行首地址 a[1], *(a + 1) 第1行,0列元素地址 a[1] + 2, *(a + 1) + 2, & a[1][2] 第1行,2列元素地址 *(a[1] + 2), *(*(a + 1) + 2), a[1][2] 第1行,2列元素的值

3.5 函数指针

函数指针是指向函数的指针,指针可以指向变量,数组,也可以指向一个函数。
每一个函数在编译的时候会分配一个入口地址,这个入口地址就是函数指针,函数名称就代表函数的入口地址(和数组类似)。

1)函数指针的定义方式:

函数返回类型 ( * 指针变量名称 ) ( 参数列表 )

int (*p)(int);  //定义了一个指向int func(int n)类型函数的指针

2)函数指针的特点:

函数可以通过函数指针调用
使用函数指针可以实现根据条件调用不同的函数:

void man(){    printf("抽烟\n");    printf("喝酒\n");    printf("打牌\n");}void woman(){    printf("化妆\n");    printf("逛街\n");    printf("网购\n");}int main(){    //声明一个函数指针,此时函数指针没有明确的指向    void(*p)();    int i = 0;    scanf("%d", &i);    if (i == 0)        p = man;    else        p = woman;    //调用函数p    p();    return 0;}

在回调函数和运行期动态绑定的时候大量的用到了函数指针

回调函数

回调函数就是通过函数指针调用的函数。如果把函数的指针作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这个被调用的函数为回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
如下例中,max函数和add函数均为回调函数,func根据不同的情况,有选择性的调用两个函数中的一个

int max(int a, int b){    if (a > b)        return a;    else        return b;}int add(int a, int b){    return a + b;}void func(int(*p)(int, int), int a, int b){    int res = p(a, b);    printf("%d\n", res);}int main(){    int  i = 0;    scanf("%d", &i);    if (i == 0)        func(max, 10, 20);    else        func(add, 10, 20);    return 0;}

四.指针运算

指针运算不是简单的整数加减法,而是以指针指向的数据类型在内存中占用字节数的倍数的运算,指针运算的实质是指针在内存中指向的移动。

char *p;p++;        //移动sizeof(char)个字节数int *p1;p1++        //移动sizeof(int)个字节数p1+4;       //p1移动4个长度单位(4*sizeof(int)的长度)

通过指针使用数组元素

指针的加减运算可以使指针移动,所以指针可以非常方便的操作数组

int a[10];int *p = a;p + 1;  //代表&a[1]p + 5;  //代表&a[5]