1. 字符指针变量
我们都知道指针可以指向各个变量的地址,其中自然也可以指向字符变量,我们便叫它字符指针变量。其本质是指向首字符的地址的变量
以这段代码为例,pstr里存的实际上是首字符 ‘h’ 的地址。
我们再来看这么一段代码。
为什么答案会不一样呢?原来由于str3和str4都指向了同一个常量字符串,而C/C++会把常量字符串存储到单独的⼀个内存区域, 当⼏个指针指向同⼀个字符串的时候,他们实际会指向同⼀块内存,因此地址是一样的,但是⽤相同的常量字符串去初始 化不同的数组的时候就会开辟出不同的内存块。所以str1和str2不同,str3和str4相同。
2. 数组指针变量
2.1 数组指针变量是什么?
虽然之前好像也问过类似的问题,但我还是要再问一次,数组指针变量是数组还是指针?
答案是:指针。
即然整形指针变量存放的是整形的地址,那么数组指针变量存放就应该是数组了。
那么请问下面的p1,p2分别是什么呢?
我们先看p1,p1先与 后面的[10]结合,p1[10],表示一个数组,而它的类型就是前面的int*,类型为指针,也就是说,p1是一个可以存放10个int*类型的指针数组。
再看p2,p2先与*结合,说明它是一个指针。指向的类型为前方的int类型。但由于指向的是一个有10个元素的数组,因此后面再添上[10]。也就是说p2是一个指向有10个元素的整型数组的数组指针。
这⾥要注意:[]的优先级要⾼于*号的,所以必须加上()来保证p先和*结合。
3. ⼆维数组传参的本质
有了数组指针的理解,我们就能够讲⼀下⼆维数组传参的本质了。
过去我们有⼀个⼆维数组的需要传参给⼀个函数的时候,我们是这样写的:
那么除了这种实参是⼆维数组,形参也写成⼆维数组的形式,那还有什么其他的写法吗?
答案是有的。我们回想一下二维数组,⼆维数组其实可以看做是每个元素是⼀维数组的一维数组,那么这个数组的⾸元素也就是第⼀⾏,是个⼀维数组。
在上面参数传递中,我们可以发现传递的是数组名arr,数组名等于首元素地址,也就是说它本质上传递的还是一个地址,就意味着⼆维数组传参本质上也是传递了地址,传递的是第⼀⾏这个⼀维数组的地址,那么形参也是可以写成指针形式的。
总结:⼆维数组传参,形参的部分可以写成数组,也可以写成指针形式。
4. 函数指针变量
4.1函数指针变量的创建
经过前面的学习,我们可以很快的得出结论,函数指针是一个指向函数地址的指针,但函数有指针吗?我们来做一个测试。
事实证明函数也是有地址的。而且函数名就是函数的地址,当然也可以通过&函数名的⽅式获得函数的地址。
那么函数指针变量又该如何创建呢?
原函数该有的参数一个都不少,但在原函数函数名的地方,要改成自己想要的函数指针名。
4.2函数指针变量的使⽤
4.3.1typedef关键字
typedef是⽤来类型重命名的,可以将复杂的类型,简单化。
比如
指针类型也是同理
这里就是将int*重命名为ptr_t 。
但是对于数组指针和函数指针稍微有点区别:⽐如我们有数组指针类型int(*)[5],需要重命名为ptrr_t
新的类型名必须在 * 的右边
函数指针类型的重命名也是⼀样的,⽐如,将void(*)(int)类型重命名为pf_t,就可以这样写:
5. 函数指针数组
把函数的地址存到⼀个数组中,那这个数组就叫函数指针数组,那函数指针的数组如何定义呢?
以这段代码为例,parr1先和[]结合,说明parr1是数组,数组的内容是什么呢?是int (*)()类型的函数指针。
要是看不懂的话可以这么理解,将原函数名的修改成什么类型就是函数xx类型,比如原函数名的位置改成指针那就是函数指针类型,改成指针数组就是函数指针数组类型,是可以进行套娃的。
6. 转移表
有人可能会想,函数指针数组有什么用,转移表就用到了函数指针数组 ,我们来看一下下面这段简易计算机的代码。
#include <stdio.h>
// 四个基本的数学运算函数
int add(int a, int b) {
return a + b;
}
int sub(int a, int b) {
return a - b;
}
int mul(int a, int b) {
return a * b;
}
int div(int a, int b) {
// 检查除数是否为0
if (b == 0) {
printf("除数不能为0\n");
return 0; // 或者你可以考虑抛出一个错误
}
return a / b;
}
int main() {
int x, y;
int input = 1;
int ret = 0;
// 函数指针数组(转移表)
int(*p[5])(int, int) = { NULL, add, sub, mul, div };
do {
printf("*********\n");
printf(" 1:add 2:sub \n");
printf(" 3:mul 4:div \n");
printf(" 0:exit \n");
printf("*********\n");
printf("请选择:");
scanf("%d", &input);
// 确保用户输入的是有效的操作码
if (input >= 1 && input <= 4) {
printf("输入操作数:");
scanf("%d %d", &x, &y);
// 调用对应的函数
ret = p[input](x, y); // 注意这里不需要解引用(*),因为p[input]已经是函数指针了
printf("ret = %d\n", ret);
} else if (input == 0) {
printf("退出计算器\n");
} else {
printf("输入有误\n");
}
} while (input); // 当用户选择退出(input为0)时,循环结束
return 0;
}
在这里函数指针数组p就起到了个转移表的作用,类似于中介,不仅接收了需要的函数,也接收了需要的两个操作数。