🏖️作者:@malloc不出对象
⛺专栏:《初识C语言》
👦个人简介:一名双非本科院校大二在读的科班编程菜鸟,努力编程只为赶上各位大佬的步伐🙈🙈
目录
- 前言
- 一、基本数据类型指针
- 1.1 指针的解引用
- 1.2 指针的基本运算
- 1.2.1 指针+-整数
- 1.2.2 指针 - 指针
- 1.3 字符指针
- 1.4 字符串与字符数组的联系
- 二、指针数组
- 三、数组指针
- 四、函数指针
- 4.1 函数名就是地址吗
- 五、函数指针数组
- 六、指向函数指针的数组
前言
之前的一篇文章也是带大家初步的认识了一下指针,那么这篇文章呢主要就是介绍各种指针类型的基本用法,让你一次性搞清各种指针类型的用法。
一、基本数据类型指针
我们知道,变量有不同的类型,整型、浮点型等,那指针有没有类型呢?准确的说:有的。那大家还记得我们上篇讲的指针变量与指针的区别嘛?这里的"指针"到底是什么呢?好了,相信只要看过上篇文章就一定知道此时的指针其实是为指针变量,变量才有类型,地址就是一个数值你能看的出它是代表什么类型吗?这里就稍微的带大家回顾一下上篇的知识点,我们平常还是口头上说指针比较顺口吧。
之前我们提到过指针的大小在32位平台下占4个字节,64位平台下占8个字节,那么指针类型的意义到底在哪呢?它们的大小不都是一样大的嘛?
我们来看下图:
从这幅图我们看到也看到不同类型的指针类型它们所指向的地址也是一样的,这里其实是会有一个警告的,int*和char*的类型不兼容,但这并不影响他们的地址是一样的,pa和pc两者都是指针变量,能够存放的字节大小都为4or8.
既然不同指针类型的大小和所指地址都一样那我们要分成这么多指针类型呢?用一个不就好了吗?下面我们继续进行分析.
1.1 指针的解引用
在上一篇文章指针的初步认识部分我只是简单的讲了一下解引用,下面我将对这部分进行着重讲解。
下面我们来看一个例子,对指针进行解引用会得到什么呢?
通过调试我们来观察一下整型a在下内存的变化,a的内容在内存中是倒着存的,VS是小端模式,低地址在低位,高地址在高位:
下面我们进行下一步:
这时我们发现通过整型指针pa的解引用,a的四个字节全都置为0了,大家还记得我在上一篇文章解引用那块说了一个什么结论吗?
在同类型情况下对指针解引用,代表指针所指向的目标。 那么对于整型指针pa来说就非常好理解了,*pa = 0 ==> a = 0。
下面我们一起来看看字符指针pc进行解引用会对a产生什么效果:
这里我们发现整型a只有一个字节(首字节)变为0,其他三个字节不变,那么这说明了什么问题呢?
结论:指针的类型决定了对指针解引用的时候有多大的权限(能操作几个字节)。
1.2 指针的基本运算
1.2.1 指针±整数
我们来看一个例子,大家想想会得到什么结果:
#include<stdio.h>
int main()
{
char* c = NULL;
short* s = NULL;
int* i = NULL;
double* d = NULL;
printf("%d\n", c);
printf("%d\n\n", c + 1);
printf("%d\n", s);
printf("%d\n\n", s + 1);
printf("%d\n", i);
printf("%d\n\n", i + 1);
printf("%d\n", d);
printf("%d\n\n", d + 1);
return 0;
}
下面给出结果:
由此我们得出结论:指针的类型决定了指针 ± 向前或者向后走一步有多大(距离)。
下面接着我们来看一个例子,这段代码得到的又是什么呢(x86环境下)?
#include<stdio.h>
int main()
{
char* c = NULL;
char** q = NULL;
char*** d = NULL;
printf("%d\n", c);
printf("%d\n\n", c + 1);
printf("%d\n", q);
printf("%d\n\n", q + 1);
printf("%d\n", d);
printf("%d\n\n", d + 1);
return 0;
}
下面给出答案:
根据上面得出来的结论:我们知道指针+1本质上是加上指针所指向的对象类型的大小,第一个我就不多说了,第二个指针所指向的类型为char*,那么它占多大呢?它所指向的对象为指针变量,在x86环境下是4个字节大小,x64下是8个字节大小;第三个指针所指向的类型为char**,它所指的对象是不是也为一个指针变量呢?所以它也是占4字节。
由此我们又可以得到一个小结论:一级指针+1是会随着所指类型改变而改变的,而二级及以上的指针+1是不会发生变化的,在x86环境下+1移动4个字节的单位,在x64环境下+1移动8个字节的单位。
1.2.2 指针 - 指针
看着我的小标题大家也许会想为什么不会出现指针+指针呢?下面我给大家解释一下:
C/C++中禁止两个指针相减,只能两个指针相加。这样做的原因是两个指针相减给出了一个逻辑上可以解释的结果,两个指针相减得到的是两者之间的内存偏移量。同样,你也可以在指针中±一个整数,这意味着“向上或向下移动指针”。而指针加指针是很难解释的,得到的新指针表示什么?如果你明确需要一个指向内存中某个位置的指针,该位置的地址是其他两个地址的总和,则可以将这两个指针强制转换为某种数据类型,将其相加后的结果强制转换为指针。但请记住此解决方案需要非常注意指针算法,并且真的不应该这样做。
更通俗的其实可以这样来理解:指针是用来代表内存地址的,指针的数值是该地址相对于最低位地址也就是0位地址的偏移量,也可称之为坐标。坐标相加得到的新值是没什么意义的,坐标相减则是两者距离,这是有意义的。
接下来我们来看一个实例,指针减指针得到的是什么呢?
#include<stdio.h>
int main()
{
int arr[10]={1,2,3,4,5,6,7,8,9,10};
printf("%d\n",&arr[9] - &arr[0]);
return 0;
}
让我们看向结果:
结果为9说明什么,得到的是元素的个数,我们通过上面的结论也可以理解此时&arr[9]和&arr[0]相当于两个坐标,坐标减坐标自然得到的是它们两者相差的距离,也就是本题当中的元素个数。最简单的理解方式也可以是这样,还记得我们上篇文章提到过要想访问一个数组元素是怎样来进行访问的吗?我们说过要找到某一个目标必须得根据它的起始地址来进行偏移寻找,&arr[9] == arr + 9,&arr[0] == arr,俩者相减是不是就为它们之间的元素个数了呢🙈🙈
当然一定会有人这样来想,&arr[9] - &arr[0]为什么得到的是元素个数啊?它们得到不是一个地址差值嘛,也就是36呢? 首先说明一下这是C语言标准规定的:在同一块空间内同等类型的指针相减得到的就是之间的元素个数。
至于为什么不是36而是9?我们可以这样来想,36得到的是字节之差,此时单位为字节,,而此时得到的是9,也就是36 / 4 = 9,此时的单位是数组长度。这里你可以理解为C语言对它进行了细节处理,此时指针-指针的单位是数组长度而不是字节,所以最后我们看到的答案就是元素个数了。
下面是一幅简单的图来表示关系:
1.3 字符指针
关于基本数据类型指针我最想讲的是字符指针,这其中牵扯到字符串与字符数组之间的联系,我想好好给大家讲一讲这其中的一些重要知识点。其他的基本数据类型指针就不做过多的阐述了…
首先我们先来看看最简单的用法:
#include<stdio.h>
int main()
{
char ch = 'a';
char* pch = &ch; //字符指针 --> 是一个指针,保存的是字符ch的地址,它所指向的对象的类型为char型
printf("%d\n", *pch);
return 0;
}
下面我们再来看一段代码,当字符指针变量接收字符串时:
#include<stdio.h>
int main()
{
char* pch = "abcdefg";
printf("%s\n",pch);
return 0;
}
此时字符指针变量pch接收到的是整个字符串的地址还是字符串的首字符的地址?我们不妨来检测一下:
从图中我们可以看出以当对pch进行解引用时,我们用%c打印出来的是首字符,所以此时证明字符指针变量pch接收到的是首字符的地址,本质上字符串其实就是首字符的地址。
1.4 字符串与字符数组的联系
字符串是什么?
用" "双引号括起来的就是一个字符串,字符串是以’\0’结尾的,它的末尾会自动加上一个’\0’,‘\0’(转义字符)为字符串结束的标志。
C语言中是没有字符串这种数据类型,但可以通过字符数组或字符指针来表示,那么字符数组就一定是字符串吗?
不是的,其实字符数组和字符串是两个完全不一样的概念,从存储位置来看字符串在静态存储区中,所以它是不能被修改的,而字符数组是在堆/栈区上开辟的一块连续的空间它的元素是一个个的字符元素,它是内容是能够被修改的,并且字符数组不一定表示的就是字符串。
关于这些性质,下面我们下面就来一一证实一下。
我们来看看下面一段代码,大家觉得能修改成功吗?
#include<stdio.h>
int main()
{
char* pch = "abcdefg";
*pch = 'W';
printf("%c\n",*pch);
return 0;
}
我们一起来看看结果:
从结果上来看程序已经崩溃了,因为字符串常量它是不可被修改的,它是放在静态区的。
下面我们来看看用字符数组来表示字符串是否能够修改字符数组中的内容:
从图中我们可以看到字符数组中的内容它是能被修改的,因为此时字符数组ch是在栈上开辟的一段空间,保存的是一个个字符元素,并且由于右值是一个字符串它的末尾也会自动添加一个\0,此时数组ch的元素个数其实是为8个,我们通过监视窗口一起来看看:
下面我来通过一张图让大家彻底明白为什么用字符指针变量表示的字符串它的内容是不可修改的,而用字符数组来表示它却能修改其中的内容。
*pch代表的其实就是字符串的首字符a,它位于字符串常量区只能作为右值只提供内容,而不能提供空间,因此它是不能被修改的,,而字符数组是在栈上开辟的一块连续的空间,它的内容是能被修改的。
那么如果我就是想改变指针变量所保存的内容呢?我们可以改变指针的指向,这样得到的字符串内容就不一样了:
我们继续看下一个问题,为什么我前面说过字符数组不一定表示的就是一个字符串呢?下面我们一起来看这个例子,它会打印出什么东西呢?
#include<stdio.h>
int main()
{
char ch[] = { 'a', 'b', 'c', 'd' };
printf("%s\n", ch);
return 0;
}
我们来看一下结果:
结果打印出乱码来了这是为什么呢?
%s是将字符串格式化输出的,那么此时你看这个字符数组能表示成一个字符串吗?答案是不能的,因为此时它的末尾没有\0表示字符串结束的标志,而%s是根据地址来进行一个个寻找的,直到遇到\0就停止寻找输出结果。要想表示成一个字符串我们需要在最后一个位置放上一个\0(0)表示字符串结束的标志,这样就没有任何问题了。
此时我们也可以得出一个小结论:以\0(0)结尾的字符数组可以看做一个字符串,如果没有以数字0结尾那就不是一个字符串。还需要注意此"字符串"非真正意义上的字符串。
接下来我们来做一道题,如果你听懂了上述我的一系列讲解那么这道题一定没问题:
#include<stdio.h>
int main()
{
char str1[] = "hello hhncu.";
char str2[] = "hello hhncu.";
char* str3 = "hello hhncu.";
char* str4 = "hello hhncu.";
if (str1 == str2)
printf("str1 and str2 are the same\n");
else
printf("str1 and str2 are not same\n");
if (str3 == str4)
printf("str3 and str4 are the same\n");
else
printf("str3 and str4 are not same\n");
return 0;
}
我们给出结果:
下面我还是带大家分析一遍,只需要下面一幅图大家就能理解了:
根据这幅图来分析其实很简单,str1[]和str2[]是两个不同的数组,我们在栈上开辟的是两块不同的空间,虽然它们存放的内容是相同的,但它们不属于同一块空间,所以str1和str2肯定是不一样的。
我们再来分析一下str3和str4,它们存放的都是一个常量字符串的地址,常量字符串里面的内容又不能被修改,所以没必要存两份一样的字符串,但是str3和str4还是两个变量,只是它们指向同一块空间。
关于基本数据类型指针就给大家介绍到这里了。
二、指针数组
int arr[10]; //整型数组,存放整型的数组
char ch; //字符数组,存放字符的数组
那指针数组又是什么呢?
类比推理,它是一个存放指针(地址)的数组。
下面我们来看一下它的基本应用:
#include<stdio.h>
int main()
{
int a = 10;
int b = 20;
int c = 30;
int* arr[3]={&a, &b, &c}; //这里[]的优先级比*高,所以arr与[]结合变为了一个数组,数组元素的类型为int*型,也就是数组元素为指针(地址)
for(int i = 0; i < 3; i++)
{
printf("%d ",*(arr[i])); //arr[i]表示的是地址,再进行解引用得到的就是相应的abc的值
}
return 0;
}
下面来看看它适用的环境:
#include<stdio.h>
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 };//这里面存的是三个数组名,也就是三个数组的首地址
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 5; j++)
{
printf("%d ", *(parr[i] + j));//parr[i]是三个一维数组的首地址+j表示这个往后移动几个单位,这样就能够依次打印出三个数组中的所有元素
}
putchar('\n');//打印换行符
}
return 0;
}
我们来看下效果:
三、数组指针
介绍完指针数组我们来看一看数组指针,数组指针是数组还是指针呢?同样的我们使用类比的方法进行推理。
int* p = NULL; //p是整型指针 -指向整型的指针 - 可以存放整型的地址
char* pc = NULL; //pc是字符指针 -指向字符的指针 - 可以存放字符的地址
数组指针就是指向数组的指针,可以存放数组的地址。
如何快速的知道它是数组还是一个指针呢?
这里我们有个小技巧:看最后一个名词即可判断出它是指针还是数组,前面的都是语文中的形容词,都是用来修饰最后一个名词的。例如:数组指针是一个指针、指针数组是一个数组、函数指针是一个指针、函数指针数组是一个数组…
下面来看一看数组指针的基本用法:
#include<stdio.h>
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int(*pa)[10] = &arr; //由于[]的优先级高于*,所以我们要想表示它为一个指针就应该将它括起来,表示pa是一个指针,它所指向的数组大小为10,数组元素的类型为int型
//&arr -- > pa = &arr,*pa = *&arr,*和&一抵消就是arr数组名了
for (int i = 0; i < 10; i++)
{ //以下的两种表达方式都可
printf("%d ", *(*pa + i));//pa存放的是整个数组的地址,对它进行解引用其实得到的是arr数组名(数组首地址),arr + i得到的是i的地址,最后进行解引用得到的就是数组元素
//printf("%d ", (*pa)[i]);//我们可以用这样来表示数组的元素arr[i],既然这里*pa=arr,那么也就可以这样来表示(*pa)[i])
}
return 0;
}
其实打印一维数组用数组指针来做还有点麻烦,我们采用int* p = arr;这种方式不更简单嘛,循环里面直接用*(p + i)或者p[i]就能实现了。
下面我们来看数组指针的实际应用场景,打印二维数组中的元素:
#include<stdio.h>
int main()
{
int arr[3][5] = { {1,2,3,4,5},{6,7,8,9,10},{11,12,13,14,15} };
int(*p)[5] = arr; //数组指针p它所指向的数组大小为5,数组元素的类型为int型,数组名arr表示的是首地址
//将二维数组看成一维数组的话,那么它有三个元素(一维数组)分别为arr[0]、arr[1]、arr[2],它们都为数组名都为一维数组的首地址。
//那么二维数组的首地址就为&arr[0],arr[0]为一个一维数组,&arr[0]就为第一行(第一个一维数组)的地址
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 5; j++)
{
//一下任选一种都可
//printf("%d ", p[i][j]); //p = arr --> arr[i][j] ==> p[i][j]
//printf("%d ", (*(p + i))[j]);//这里可以类比一维数组,*(p+i)得到的是第i个元素,在二维数组中第i个元素就为第i个数组-->p[i][j] == arr[i][j]
//printf("%d ", *((p[i] + j)));//p[i]表示第i行,+j就表示第i行第j列元素的地址,最后进行解引用得到第i行第j列元素的值
printf("%d ", *(*(p + i) + j));//*(p+i) == p[i],同上
}
putchar('\n');
}
return 0;
}
关于数组指针的使用我们再来看一个例子:
char* arr[5];
&arr做为右值的话左边该如何去写?
我们来分析一下:我们要存放数组的地址是不是要一个指针变量来存,所以我们写成(*p)表示它为一个指针,接着看我们的p指向的数组是不是有5个元素,所以我们写成(*p)[5],再看着char* arr[5]它为一个指针数组,数组中的每个元素的类型是char*,所以我们p指向的数组的元素类型应该为char*型,最后我们就写成这样来表示:char* (*p)[5] = &arr;
下面看一段代码再来加深一下印象,你能准确说出它们的类型吗?
int arr[10]; //这是一个整型数组,arr数组有10个元素,每个元素的类型为int
int* parr1[10]; //parr1为一个指针数组,parr1与[10]结合说明他为一个数组,该数组的元素个数有10个,每个元素的类型为int*,它是一个存放指针的数组。
int(*parr2)[10]; //parr2为一个数组指针,parr2先与*结合说明它是一个指针,它所指向数组的元素有10个,每个元素的类型为int,它可以用来存放数组的地址
int(*parr3[10])[5];//parr3先与[10]结合说明它是一个数组,这里有个小技巧parr3与[10]结合成为数组之后,剩下的部分就是它的类型了int(*)[5],它又是一个数组指针。
//parr3是一个数组,该数组有10个元素,每个元素是一个数组指针,该数组指针指向的数组有5个元素,每个元素的类型为int
我们通过画图来展示一下parr3:
四、函数指针
大家先思考一下,整型、字符型、数组都有地址,那么函数也有地址吗?
答案是有的,函数是什么呢?函数是由一些运行的语句组成的,一个函数被编译后就成为一系列的指令,当程序被调用时,函数指令代码在内存中占据一片存储单元。当函数被调用时,流程跳转到该函数内继续运行,调用该函数时执行的第一条指令的地址称为函数的入口地址,通过这个地址可以找到该函数。函数代码在内存中开始的那个内存空间的地址就是函数的地址!!
函数是存储在代码区的,至于函数中用的变量的地址并不是放在代码区的,一般都放在另外的两个地方即栈区与堆区,所以在地址上是有很大的差值的。
4.1 函数名就是地址吗
我们通过一个例子来检测一下,同时我们想一下Add和&Add两者是否是一样的呢?arr表示的是数组名(首地址),&arr表示数组的地址,那么函数名也有这一说法嘛?
int Add(int x,int y)
{
int z = 0;
z = x + y;
return z;
}
int main()
{
int a = 10;
int b = 20;
printf("%p\n",Add);
printf("%p\n",&Add);
return 0;
}
看下图结果:
从上图我们可以发现Add和&Add在数值上是相等的,那么是否可以认为函数名 == &函数名,函数名就是地址呢?
切不可把函数名认为就是地址,函数名并不是函数地址的代表,这种误解与数组名就是指针一样犯了相同的错误。函数名是函数实体的代表,不是地址的代表。当然,你马上就会有疑问,平时我们不都是把函数名作为函数的地址吗?是的,函数名可以作为函数的地址,但是绝大多数人都忽略了一个条件,从函数到指针的隐式转换是函数名在表达式中的行为,就是说这个转换仅在表达式中才会发生,这仅是函数名众多性质中的一个而非本质,函数名的本质就是函数实体的代表。
到了C++,由于C++规定,非静态成员函数的左值不可获得,因此非静态成员函数不存在隐式左值转换,即不存在像常规函数那样的从函数到指针的隐式转换,所以必须在非静态成员函数前使用&操作符才能获得地址。
关于数组与指针之间的牵扯我后面会跟大家进行详细的讲解,接下来我们就来看看函数指针的用法:
#include<stdio.h>
int Add(int x,int y)
{
int z = 0;
z = x + y;
return z;
}
int main()
{
int a = 10;
int b = 20;
int(*p)(int,int) = Add; //这里写成Add和&Add都可,因为它们在数值上相等
//函数指针的写法类比数组指针,首先它是一个指针,所以写成(*p)表示p是一个指针变量
//指针所指向的是一个函数,函数的两个形参为int型,所以我们写成这样(*p)(int, int),,最后Add函数的返回类型为int型,所以最终我们写成int (*p)(int, int) = &Add.
printf("%d\n", (*p)(a, b));
return 0;
}
我们来验证一下结果,答案是没有问题的:
接下来我们继续来看一个例子,这是我在调试窗口中看到一个的奇怪现象:
#include<stdio.h>
void Print(char* str)
{
printf("%s\n", str);
}
int main()
{
void (*p)(char*) = &Print;
(*p)("hello hncu!");
printf("%p\n", p);
printf("%p\n", &Print);
printf("%p\n", *p);
printf("%p\n", Print);
return 0;
}
首先我们来看看结果,结果是没有什么问题的,它们四个在数值上是相等的
接下来我们再监视窗口看到的奇异现象:
我们发现在监视窗口的地址跟打印出来的地址不一样?这是为什么呢?
这是因为此时函数指针变量p保存的是代码区的函数地址,而在监视窗口看见的Print的地址是栈上的,p在存储Print的入口地址时并没有在栈上开辟空间,因此保存的是函数代码区的地址,这是为了方便寻找函数。
关于函数指针还有两个例子下面我们一起来看看,我们该如何理解下面的代码?
//代码1
(*(void(*)())0)();
//代码2
void(*signal(int,void(*)(int)))(int);
以上例子出自《C陷阱与缺陷》,感兴趣的读者可以详细读一下这本书,也是非常的经典。
下面我们来分析一下这道题:
首先看到代码一,我们先从最里面开始入手,红色划线部分void(*)()它是一个函数指针,接着我们看到它是用括号括起来的,我们就想到了强制类型转换,这里是将0强制类型转化为函数指针型,此时0就是一个函数的地址,再进行解引用找到某个函数,调用该函数,该函数的类型为void,并且无参。
这段代码的作用就是调用0地址处的该函数。
再来看下第二段代码,void(* signal(int , void(*)(int) ) ) (int);首先我们观察一下发现signal是一个函数名,它的第一个参数为int型,第二个参数为 void(*)(int)–>函数指针。这里我们举个例子int Add(int,int),Add是函数名,它的两个参数为int型,它的返回类型为int,这里还有个小技巧我们把Add(int,int)这部分去掉就得到了它的返回类型int了。类似的我们去掉signal后面的部分得到的void(* ) (int)就是它的返回类型了,它的返回类型也为一个函数指针。
最终我们这么来解释这段代码:signal是一个函数声明,它的函数参数有两个第一个是int,第二个是函数指针,该函数指针的参数类型为int,返回类型为void;signal函数返回类型也为一个函数指针,该函数指向的函数参数为int,返回类型为void。
大家有没有发现有个问题,为什么int Add的返回类型写在前面,而这个代码却写成void(*signal(int,void(*)(int)))(int)这个样子,为什么不写成void(*)(int) signal(int,void(*)(int))这样的形式呢?
这里跟我们的指针有关,*表示他为一个指针,变量和函数名要贴近*先与他结合在一起才表示为一个指针,就如int(*p)();*先与p结合才表示它为一个指针。
这段代码看起来确实有些复杂我们能不能进行简化一下呢?
提到简化你会想到什么,#define还是typedef,这里我们采用typedef来进行简化,而不采用#define进行文本替换,我们在之前也讲过#define在预处理阶段就已经将文本替换好了,它不进行任何语法语意以及类型的检查,一旦我们的类型出现任何问题我们很难迅速的找到问题所在之处,所以这里采用typedef进行类型重命名替换。
按我们平常就应该是这么typedef void(*)() pfun_t;写的了,而对于我们的函数指针来说不是这样的,它有自己的规定,我们应该这么来写:typedef void(*pfun_t)();最终我们就简化写成这种形式:pfun_t signal(int , pfun_t);
五、函数指针数组
我们知道数组可以连续存放相同类型的元素,指针数组是一个可以连续存放指针的数组,那么我们推断函数指针数组就是能连续存放函数指针的一个数组。在运用多种函数的情况下非常适用。我们来举个例子:
#include<stdio.h>
int Add(int x, int y)
{
int z = 0;
z = x + y;
return z;
}
int Sub(int x, int y)
{
int z = 0;
z = x - y;
return z;
}
int Mul(int x, int y)
{
int z = 0;
z = x * y;
return z;
}
int Div(int x, int y)
{
int z = 0;
z = x / y;
return z;
}
int main()
{
int (*parr[4])(int, int) = { Add, Sub, Mul, Div };//函数指针数组,parr先与[4]结合表示它为数组,接着我们不看这一部分剩下的就是该数组中每个元素的类型了,int(*)(int,int)-->函数指针,表示该函数所指的参数类型为int,int型,该函数的返回类型为int型。
for (int i = 0; i < 4; i++)
{
printf("%d\n", parr[i](2, 3));//parr[i]代表函数名
}
return 0;
}
此部分就是简单的实现了一下计算器的功能,利用函数指针数组调用不同的函数去实现对应的功能
六、指向函数指针的数组
int arr[10] = { 0 };
int(*p)[10] = &arr;//取出数组的地址
int(*pf)(int,int);//函数指针
int(*pfArr[4])(int,int);//pfArr是一个数组 - 函数指针的数组
int(*(*ppfArr)[4])(int,int) = &pfArr;//pfArr是一个指向函数指针数组的指针,(*pfArr)说明它是一个指针,(*pfAff)[4]说明该指针指向的数组有4个元素,每个元素的返回类型为int(*)(int,int)
关于指向函数指针数组的指针其实这部分用的并不多,稍微了解一下即可。
今天的文章就到这了吧,由于篇幅问题我把回调函数这块的内容放到下一篇文章中进行详细讲解,最经典qsort函数就是用回调函数来实现的。关于文章如果有任何疑问或者错处欢迎大家评论区进行交流哦orz~🙈🙈