一、数组指针变量
在上一节中我们提到了,指针数组的存放指针的数组,那数组指针变量是什么呢?
显而易见,数组指针变量是指针
同样类比整型指针变量和字符指针变量里面分别存放的是整型变量地址和字符变量地址,我们可以知道,数组指针变量中存放的是数组的地址,是可以指向数组的指针变量。
int *p1[10];
int (*p2)[10];
对比上面的p1和p2,谁是数组指针变量?
int (*p2)[10];
这里需要知道:[ ] 的优先级高于 *
要想是指针变量,就需要* 和 p 先结合,所以需要括号改变运算顺序。
那 int *p1[10]; 是什么呢?
这就是我们上一节讲的指针数组。
1、数组指针变量如何初始化
数组指针变量是用来存放数组的地址的,那数组的地址是如何获得的呢? &arr就可以了。
如果需要存放一个数组的地址,就需要存放在数组指针变量中
int (*p)[10] = &arr;
需要注意!
1、
int *p[10] = &arr;
p++ == arr + 1;
2、
int (*p)[10] = &arr;
p + 1 != arr + 1
第二种的p+1是跳过整个数组
2、二维数组传参的本质
先观察下面的test1 和 test2
void test1(int arr[3][5],int r,int c) {for (int i = 0; i < r; i++) {for (int j = 0; j < c; j++) {printf("%d ", arr[i][j]);}printf("\n");}}void test2(int(*p)[5], int r, int c) {for (int i = 0; i < r; i++) {for (int j = 0; j < c; j++) {printf("%d ", *(*(p + i) + j));}printf("\n");}}
int main() {int arr[3][5] = { {1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7} };test1(arr, 3, 5);printf("\n");test2(arr, 3, 5);return 0;
}
两者的输出结果一样
根据数组名就是数组首元素地址这个信息,我们可以推论出,二维数组的数组名就是第一行的地址,也就是第一行数组的地址。
意味着:二维数组的传参本身是传地址,传递的是第一行的一维数组的地址
三、函数指针变量
创建函数指针变量
通过前面多次的类比学习,我们可以和轻易的得到,函数指针变量就是存放函数地址的指针,通过指针去指向函数的地址来调用函数。
printf("test = %p\n", test1);printf("&test = %p\n", &test1);
我们在刚刚的main函数中加入这两句代码,来帮助我们了解函数是否存在地址
可以得出:函数是有地址的,而且因为两者的地址相同,所以函数名就是函数的地址、
拿到了函数的地址,我们就只需要将其存放起来,就可以创建函数指针了
观察下面的代码:
void test() {printf("haha\n");
}int Add(int x,int y) {return x + y;
}int main() {void (*pf1)() = test;void (*pf2)() = &test;int (*pf3)(int x,int y) = Add;int (*pf4)(int ,int ) = &Add;(*pf1)();(*pf2)();printf("%d\n", (*pf3)(3,4));printf("%d\n", pf4(4,5));return 0;
}
对于test和add函数的调用这次没有使用函数名,而是使用函数指针调用。
同时我们还可以通过运行结果看出,使用函数指针去调用函数的时候,是不用解引用指针名的
如: pf4(4,5) 依然得到了正确结果9。