目录
一、指针与数组
1.1指针数组
数组是我们之前已经学过的知识,我们知道数组有整形数组,字符数组等等,那么显而易见本质上指针数组就是数组,只不过数组里面装元素的是指针类型(地址)。指针数组的格式是type *p[n],依据优先级,先看 [n] ,表示是存放n个元素的数组,前面的 * 表示里面存放的是指针类型。
我们知道可以通过下标访问数组中元素,而指针是指向变量的地址,那么我们就可以将数组中元素的地址赋值给指针并存放在数组里,之后我们就可以调用这个指针数组来操作原数组。而在数组中,数组名就是该数组的首地址,那么结合上篇文章所说的指针和整数的相加减加减,我们就可以实现指针访问数组元素。
在上面的代码中我们可以看到,指针数组str里面的每一个值所指向的地址都是和arr数组所对应,str里面的每一个值的类型都是整型指针 int* 类型。我们需要注意的是,在对指针进行赋值时,一定要使用取地址&符号,保证赋值号两边的数据类型相同的,不然会提示错误信息。
1.2数组指针
数组指针和上面一样,最后的词决定它是什么,所以数组指针本质上是指针,是指向数组的指针。数组指针的写法是type (*p)[n],我们要知道 [] 的优先级是高于*号的,所以我们要使用小括号让 p 先与 * 结合,表明 p 是一个指针变量,之后与 [n] 结合表明这是一个指向 n 个元素的数组,所以这是一个指向含有n个元素的一维数组的指针,我们叫他数组指针。
看上面的代码,p1是指向含有5个元素的整型数组arr,p2是指向含有6个元素(\0)的字符型数组str,看地址可以看得出来,而当他们+1时,所跳过的就是整个数组的地址,arr数组里有五个整型元素,+1跳过剩下四个,所以地址大小+14;而str数组里有六个元素,+1跳过剩下的五个,所以地址大小+5。所以数组指针+1所跳过的大小是依据其数组类型和数组里面存放元素的数量。
数组指针在一维数组中用的比较少,一般用于多维数组,像是在二位数组中,下面我们就以二维数组为例。
定义一个3行3列的二维数组,里面存放数字1到9,将数组arr的地址赋给数组指针。那么数组指针里面的每一个元素就代表二维数组每一行元素的首地址。
在这里&arr[0][0]表示数组0行0列的地址,同时也可以看作是二维数组的首地址。&arr[n][m]表示第m行n列元素的地址,arr[n]和parr[n]表示数组中第几行元素,arr[n]+m表示第n行第m个元素地址,而parr+n表示移动到n个内存单元,这两个操作的结果是等价的。下面是课本中的一个总结,可以参考一下。
这里我们可以利用指针数组模拟实现二维数组,因为parr[i]指向的是整型一维数组,那么parr[i][j]就是在第i行中访问第j个元素,代码实现如下:
int main() { int arr1[] = { 1,2,3,4,5 }; int arr2[] = { 2,3,4,5,6 }; int arr3[] = { 3,4,5,6,7 }; int* parr[3] = { arr1,arr2,arr3 }; int i, j; for (i = 0; i < 3; i++) { for (j = 0; j < 5; j++) { printf("%d ", parr[i][j]); } printf("\n"); } return 0; }
二、指针与函数
2.1函数指针
指针与函数的关系和上面指针与数组的关系基本是一致的,所以函数指针本质上是指针,表示的是该指针所指向的地址是一个函数,声明是int (*p)(int x,int y),这里的fun是函数指针变量名,int是是p指针指向的函数的返回类型,这里的(int x,int y)表示指针指向的函数的参数类型与个数。下面我们写一段简单的代码通过函数指针调用指针指向的函数:
//函数指针 int add(int x, int y) { return x + y; } int main() { int (*p)(int, int) = add; printf("%d\n", (*p)(5, 6)); printf("%d\n", p(7, 8)); return 0; }
在这段代码中,我们写了一个加法函数,定义了一个函数指针变量指向add函数。在对指针变量进行赋值时,取&不是必需的,因为一个函数标识符就表示了它的地址,但是在函数调用,我们必须要包含一个小括号括起来的参数表,表示这指向的是一个函数。
2.2指针函数
相信大家经过前面知识的熏陶,已经知道了,指针函数本质上就是一个函数,其返回值是一个指针,写法是int* p(int x,int y);在这里*与int结合,表示p函数的返回值是int* 类型。其他的地方与普通函数的意义是一样的。下面我们写一段代码
int* add(int x, int y) { static int num = 0;//int num=0; num = x + y; return # } int main() { int *p= add(5,6); printf("%d\n", *p); return 0; }
这里和上面一样,写了一个加法函数,只不过其实现的方式不同。我们知道一般的局部变量是存放于栈区的,当函数结束,栈区的变量就会释放掉,那么当我们在函数内部定义一个变量,在使用一个指针去指向这个变量,当函数调用结束时,这个变量的空间就已经被释放,这时就算返回了该地址的指针,也不一定会得到正确的值。甚至更严重的是,如果因此访问到了不可访问的内容,很有可能造成段错误等程序崩溃的情况。因此,在使用指针函数的时候,一定要避免出现返回局部变量指针的情况。在这里我们可以把使用static修饰变量num,使其变成静态变量。静态变量的生命周期与程序的运行周期相同,因此它的值在多次调用add函数时会被保留。每次调用add函数都会返回 num 的地址,因此指针 p 指向的是同一个地址,解引用后得到的是同一个值。
三、多级指针
通过前面的学习,我们知道了指针可以指向一个普通类型的变量,如int、char、float等,那么当一个指针指向的是另一个指针是怎么样的呢?我们知道指针是一个变量,那么变量就是有地址的,地址是需要存放的,所以我们称指向指针的指针为二级指针,那么指向这个二级指针的指针呢?我们称为三级指针,以此类推,这就是我们所说的多级指针。
在这段程序中,我们可以看到a,p1,p2,p3这四个变量之间的关系,p1指向a,p2指向p1,p3指向p2,通过指针的指针,我们不仅可以访问它指向的指针,还可以访问它指向的指针所指向的数据。想要获取指针指向的数据时,一级指针加一个*,二级指针加两个*,三级指针加三个*,以此类推我们就可以得到多级指针,但是一般我们只是使用到二级指针,很少用到多级指针。
四、回调函数
回调函数是一种特殊的函数,它作为参数传递给另一个函数,并在被调用函数执行完毕后被调用。也就是说我们将某一个函数的地址用指针作为参数传递给另一个函数,当这个指针被调用时,其所指向的函数就是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用,用于对该事件或条件进行响应。回调函数通常用于实现事件处理、异步操作或用户定义的操作。下面我们通过代码来进行具体讲解:
void menu()//菜单函数 { printf("\n***********************\n"); printf("*** 1.加法 2.减法 ***\n"); printf("*** 3.乘法 4.除法 ***\n"); printf("******* 0.退出 ********\n"); printf("***********************\n"); } int add(int x, int y)//回调函数+ { return x + y; } int sub(int x, int y)//回调函数- { return x - y; } int mul(int x, int y)//回调函数* { return x * y; } int div(int x, int y)//回调函数/ { return x / y; } void calc(int (*pf)(int, int))//调用回调函数 { int x, y; printf("请输入两个操作数:"); scanf("%d %d", &x, &y); printf("运算结果为:%d\n", pf(x, y)); } int main() { int put; do { menu(); printf("请选择你的运算:"); scanf("%d", &put); switch (put) { case 1: calc(add); break; case 2: calc(sub); break; case 3: calc(mul); break; case 4: calc(div); break; case 0: printf("退出计算器\n"); break; default: printf("选择错误,请重新选择\n"); break; } } while (put); return 0; }
以上面这段代码为例,这个代码实现了一个加减乘除计算器。在这段代码中通过输入不同的数,每次都调用同一个函数calc()函数,但是每次调用calc函数所传的参数不一样,每次所传的参数都是一个算数函数,这些算数函数在在calc函数中调用,那么这些函数就称为回调函数。
回调函数的用处其实是十分之大的,在实现简单的代码时好像和普通函数没什么区别,但是仔细看,可以发现两者之间的一个关键的不同:在回调中,主程序会把回调函数像参数一样传入库函数,这也是回调函数最大的特点,这样一来,只要我们改变传进库函数的参数,就可以实现不同的功能。回调函数可以提高代码的灵活性和可重用性,尤其是在当库函数很复杂或者不可见的时候利用回调函数就显得十分优秀👍👍👍
五、总结
以上就是今天的全部内容了,主要是介绍了指针与数组、指针与函数、多级指针、回调函数,那么指针的内容就先讲这么些。如有不足欢迎家人们评论区批评指正,如果这篇文章对你有用的话,可以给我来个一键三连嘛