3.数组指针
数组指针的定义
数组指针是数组还是指针?是指针
类比:
字符指针-存放字符地址的指针-指向字符的指针 char*
整型指针 - 存放整型地址的指针 - 指向整型的指针 int*
浮点型指针 - 存放浮点型地址的指针-指向浮点型的指针 float* double*
数组指针 - 存放数组地址的指针 - 指向数组的指针
&数组名VS数组名
int main() { int arr[10] = { 1,2,3,4,5,6,7,8,9,10 }; printf("%p\n", arr);//int* printf("%p\n", arr+1);//4 printf("%p\n", &arr[0]);//int* printf("%p\n", &arr[0]+1);//4 printf("%p\n", &arr);//int(*)[10] printf("%p\n", &arr+1);//40 }
上图可看出arr==&arr[0],也说明指针类型决定了指针+1到底加的是几,即为数组名是数组首元素地址(元素1的地址),而&arr是整个数组的起始地址
int main() { int a = 10; int* pa = &a;//1 int arr[10] = { 1,2,3,4,5,6,7,8,9,10}; int(*p)[10] = &arr;//2 int(*p2)[10] = arr;//3 }
对上图总结:
1.*说明pa是指针,int说明pa指向的a变量是整型
2.&arr取出的是数组的地址,只有数组的地址才需要数组来接收,类型是int[10]
3.arr是不能放进左边指针(类型是int[10])里面的,arr本身的类型是int*
易错点
下面代码哪个是数组指针?
int *p1[10];
int (*p2)[10];
答:int (*p2)[10];
p先和*结合,说明p是一个指针变量,然后指着指向的是一个大小为10个整型的数组。所以p是一个 指针,指向一个数组,叫数组指针,[]的优先级要高于*号的,所以必须加上()来保证p先和*结合。
二维数组传参
二维数组传参两种写法
void print1(int arr[3][4],int r,int c) { int i = 0; for (i = 0; i < r; i++) { int j = 0; for (j = 0; j < c; j++) { printf("%d", arr[i][j]); } printf("\n"); } }
二维数组的指针
void print2(int(*p)[4],int r,int c) { int i = 0; for (i = 0; i < r; i++) { int j = 0; for (j = 0; j < c; j++) { /*printf("%d ", p[i][j]);*/ printf("%d ",(*(p+i))[j]);//意思是访问完第i行之后再访问[j]列 } printf("\n"); } } int main() { int arr[3][4] = { {1,2,3,4},{2,3,4,5},{3,4,5,6}}; print2(arr,3,4); return 0; }
总结:二维数组传参,形参部分可以写成数组,也可以写出指针,二维数组的数组名表示第一行的地址,arr表示第一行的地址,如果要找第一行第一列的地址,应该是&arr[0][0],(*(p+i))[j])意思是访问完第i行之后再访问[j]列,可以写成p[i][j]
易错点:
1.int arr[5];整型数组,数组有10个元素
2.int *parr1[10];指针数组,数组10个元素,每个元素都是int*类型的
3.int(*parr2)[10];parr2是数组指针,该指针指向一个数组,数组是10个元素,每个元素是int类型
4.int (*p)[10]=arr,如何强转?(int(*)[10])arr
5.int* p=&num,对p解引用的类型int
6.int(*p)[10]=&arr,&arr赋给p,*p=*&arr=arr
例题1
int(*parr3[10])[5]:
数组指针,parr3是数组,数组有10个元素,数组的每个元素的类型:int(*)[5]的数组指针类型
4. 数组参数、指针参数
4.1 一维数组传参
#include <stdio.h> void test(int arr[])//1 {} void test(int arr[10])//2 {} void test(int* arr)//3 {} void test2(int* arr[20])//4 {} void test2(int** arr)//5 {} int main() { int arr[10] = { 0 }; int* arr2[20] = { 0 }; test(arr); test2(arr2); }
1.数组传参,形参部分写出数组,可以不指定大小,指定类型即可
2.数组传参,数组接收,大小指定10个元素,可以
1.2是数组传参数组接收
3.arr[10]数组10个元素,每个元素都是int,数组名表示首元素地址(整型的地址),就应该放到int指针里去 3.是数组传参指针接收
4.数组传参可以写成一样的,大小可以不写:int *arr[]
5.数组名表示首元素地址,每个元素都是int*的地址,是一级指针地址,可以用二级指针接收,不可以写成int* arr
4.2 二维数组传参
void test(int arr[3][5])//1 {} void test(int arr[][])//2 {} void test(int arr[][5])//3 {} //总结:二维数组传参,函数形参的设计只能省略第一个[]的数字。 //因为对一个二维数组,可以不知道有多少行,但是必须知道一行多少元素。 //这样才方便运算。 void test(int *arr)//4 {} void test(int* arr[5])//5 {} void test(int (*arr)[5])//6 {} void test(int **arr)//7 {} int main() { int arr[3][5] = {0}; test(arr); }
1.可以写成一模一样的
2.二维数组传参,函数形参的设计只能省略第一个[]的数字。因为对一个二维数组,可以不知道有多少行,但是必须知道一行多少元素,这样才方便运算。
3.看2
4.数组传参,要么写成数组要么写成指针,一维指针,不对,应该是数组指针
5.数组传参,形参要么写成数组,要么指针,是个数组,但又不是二维数组,能存指针但又不是指针
6.数组前有(*arr),说明是指针,指针指向的[]是数组,数组5个元素,每个元素是int(一行五个元素)
7.看传过来的类型是什么,二级指针是用来接收一级指针的地址的,而arr传的是第一行的地址
4.3 一级指针传参
#include <stdio.h> void print(int *p, int sz) { int i = 0; for(i=0; i<sz; i++) { printf("%d\n", *(p+i)); } } int main() { int arr[10] = {1,2,3,4,5,6,7,8,9}; int *p = arr; int sz = sizeof(arr)/sizeof(arr[0]); //一级指针p,传给函数 print(p, sz); return 0; }
p是一级指针传参,一级指针接收即可
4.4 二级指针传参
#include <stdio.h> void test(int** ptr) { printf("num = %d\n", **ptr); } int main() { int n = 10; int*p = &n; int **pp = &p; test(pp); test(&p); return 0; }
5.函数指针
数组指针- 指向数组的指针
函数指针 - 指向函数的指针
int Add(int x,int y) { return x + y; } int main() { printf("%p\n", &Add); printf("%p\n", Add); return 0; }
可看到以上代码两种表示方法结果是一样的,函数没有什么首元素的地址,数组才有,所以函数指针用,&和不用&拿到的都是首元素地址,,结果都是一样,&函数名和函数名都是函数的地址
创建一个函数指针变量
int Add(int x,int y) { return x + y; } int main(){ int arr[10]; int(*pa)[10] = &arr; int (*pf)(int, int) = &Add; //int (*pf)(int, int) = &Add; }
pf是一个存放函数地址的指针变量 - 函数指针 ,和数组指针写法相似,注意:函数指针变量名是pf,不是*pf,*只是告诉我们pf是指针
调用函数指针变量
int Add(int x,int y) { return x + y; } int main(){ int(*pa)[10] = &arr; int (*pf)(int, int) = &Add //int (*pf)(int, int) = &Add int ret=pf(2,3);//int ret=Add(2,3); int ret=(*pf)(2,3) }
以上//是等价的意思,且*pf一定要加(),.否则*pf(2,3),求其结果是*5
例题
int main() { int* arr[3][3] = { 1 }; int p = **arr; printf("%d", p); }
阅读两段有趣的代码:
例题1
int main() { //代码1 (*(void (*)())0)(); return 0; }
从0开始下手,0是整型int,若在(void(*)())放p,为void(*p)()说明是函数指针,(void(*)())0说明是把0强制转换成函数指针类型(0被当成一个地址,0地址,放的参数是无参,返回类型是void的函数),
在前面加*,写成*(void(*)())0,说明对0地址做解引用操作,那个函数是无参的,所以最后面的括号写成()没有传任何参数,
总结:该代码是一次函数调用,调用的0地址处的一个函数,首先代码中将0强制类型转换为类型void(*)()函数指针,然后去调用0地址处的函数--------来自《C陷阱和缺陷》
可用上述代码类比一下 int (*pf)(int, int) <--->(*(void (*)())0)();
(*(void (*)())0)的(void (*)())0为pf部分,()为(int, int)部分
例题2
int main(){ void(*signal(int, void(*)(int)))(int); return 0; }
从signal开始,*和signal没()一起,说明不是个指针,而signal括号包含两个参数类型,一个int,另一个是函数指针,说明signal第一个参数是整型指针,第二个为函数指针,函数除了函数名和参数外,还有返回类型,把分析过的东西删去,即为返回类型:void(*)(int)
虽然这样更容易理解,但这样写是错误的:void(*)(int)signal(int,void(*)(int));(只用于方便理解)
总结:该代码是一次函数声明,声明的函数名字叫signal,siganl函数的参数有2个,第一个是int类型,第二个是函数指针类型,该函数指针能够指向的那个函数的参数是int,返回类型是void