一、数组名和&数组名
二、字符指针
1、指向字符数组首元素的字符指针 char* p=arr
2、指向常量字符串的字符指针 const char* p=”abc”
三、指针数组 int* p[3]={arr1,arr2,arr3}
四、数组指针 int(*p)[10]=&arr
五、数组传参
1、一维数组传参
2、二维数组传参
六、指针传参
1、一级指针传参
2、二级指针传参
七、函数指针
1、两个代码的理解
2、函数指针重命名
3、函数指针的使用——回调函数
八、函数指针数组——用途:转移表
九、函数指针数组指针
一、数组名和&数组名
区别:数组名表示首元素的地址,地址加1表示跳过一个数据类型的大小。
&数组名表示取出的是整个数组的地址,地址加1表示跳过一个数组的长度。
举例:arr[]需要使用整型(等)指针来指向,而&arr[]需要使用数组指针来指向。
数组名通常表示首元素的地址,但有两个例外:
1、sizeof(数组名),这里的数组名表示数组首元素的地址。
2、&数组名,表示取出的是这个数组。
二、字符指针
1、指向字符数组首元素的字符指针 char* p=arr
#include int main(){char arr[5] = "abc";char* p = arr;printf("%s", p);//打印abcreturn 0;}
数组名是首元素的地址,这里指针p指向的是arr数组的首元素’a’的地址,可以通过指针p,以%s的形式打印字符串。
2、指向常量字符串的字符指针 const char* p=”abc”
#include int main(){const char* p = "abc";//字符串abc为常量字符串printf("%s", p);return 0;}
因为”abc”是常量字符串,不能被修改,所以指针p需要使用const修饰。若没有const,.c文件运行时程序会直接挂掉,.cpp文件更加严格,会直接报错。
三、指针数组 int* p[3]={arr1,arr2,arr3}
指针数组本质上是数组,是存放指针的数组。
#include int main(){int arr1[] = { 1,2,3,4,5 };int arr2[] = { 2,3,4,5,6 };int arr3[] = { 3,7,5,8,99 };int* p[3] = { arr1,arr2,arr3 };for (int i = 0;i < 3;i++){for (int j = 0;j < 5;j++){//printf("%d ", p[i][j]);//printf("%d ", *(*(p+i) + j));//p[i]可以写成*(p+i)printf("%d ",*(p[i]+j));//解释如下图}printf("\n");}return 0;}
三种指针数组的打印方式都是可以的,这里的代码类似二维数组,该数组首元素是第一行。详细解析如下:
四、数组指针 int(*p)[10]=&arr
数组指针是指向数组的指针。
int(*p)[10]=&arr,数组指针p的类型是int(*)[10](去掉p,即为类型)。这里指针+1跳过一个数组大小。
#include print(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("%d ", *(p[i]+j));printf("%d ", *(*(p+i)+j));}printf("\n");}}int main(){int arr[3][5] = { 1,5,4,6,7,9,8,4,2,5,4,5,4,5,6 };print(arr, 3, 5);//传过去的是二维数组return 0;}
打印方式的理解与上一个例子相同,注意二维数组的首元素是第一行。
再来看一个例子:int(*p[10])[5],这是一个存放数组指针的数组,数组元素的类型是int(*)[5]。
五、数组传参
1、一维数组传参
void test(int arr[])//一维数组传参,数组接收{}void test(int arr[10])//一维数组传参,数组接收,10可有可无{}void test(int* arr)//一维数组传参,指针接收{}int main(){int arr[10] = { 0 };test(arr);}
void test2(int* arr[20])//指针数组传参,指针数组接收,20可有可无{}void test2(int** arr)//传过来的是一级指针的指针,当然可以用二级指针接收{}int main(){int* arr2[20] = { 0 };test2(arr2);}
2、二维数组传参
void test(int arr[3][5])//二维数组传参,二维数组接收{}void test(int arr[][])//错误,行不能省略{}void test(int arr[][5])//二维数组传参,二维数组接收{}void test(int *arr)//错误,传过来的是第一行的地址,相当于一维数组的地址,不能用一级指针接收{}void test(int* arr[5])//错误,不能用指针数组接收{}void test(int (*arr)[5])//可以用数组指针接收{}void test(int **arr)//错误,传过来的是一维数组的指针,不能用二级指针接收,二级指针是指向一级指针的指针{}int main(){ int arr[3][5] = {0}; test(arr);//传过来的相当于第一行的地址}
六、指针传参
1、一级指针传参
void text(int* p){}int main(){int a = 0;int* pa = &a;int arr[10] = { 0 };text(&a);//可以传整型变量的地址text(pa);//可以传一级指针的地址text(arr);//可以传整型数组的地址return 0;}
只要传过来的本质是一级指针,就可以用一级指针接收。
2、二级指针传参
void text(int** p){}int main(){int* p1 = NULL;int** p2 = NULL;int* arr[10];text(&p1);//可以传一级指针的地址text(p2);//可以传二级指针text(arr);//可以传指针数组return 0;}
七、函数指针
对于函数名与&函数名,它们的地址是一样的,且本质上没有任何区别。
使用函数值指针进行函数的调用,可以把p当成函数名调用,解引用无意义。
1、两个代码的理解
( *( void (*)() )0 )();
以上代码是一次函数调用,先将0强制类型转换为函数指针类型,该函数指针无参,返回值为void。由于函数指针解引用无意义,所以这里的这颗*可有可无。整个代码的意思是对0地址处的函数进行一次函数调用。
void ( *signal( int , void(*)(int) ) )(int);
以上代码是一次函数声明。
signal是函数名,它的参数是int和函数指针void(*)(int),返回类型是函数指针void(*)(int)。
2、函数指针重命名
typedef void(*pf_t)(int);pf_t signal(int,pf_t);
把void(*)(int)类型重命名为pf_t。
上方函数可以用pf_t来代替void(*)(int)。
3、函数指针的使用——回调函数
int Add(int a, int b){}int Sub(int a,int b){}int Mul(int a, int b){}int Div(int a, int b){}int calc(int(*p)(int,int)){return p(1, 2);}int main(){calc(Add);return 0;}
当有多个参数和返回类型均相同的函数时,可以使用函数指针对这些函数进行按需调用,减少代码冗余。
八、函数指针数组——用途:转移表
函数指针数组是存放函数指针的数组。
int Add(int a, int b){}int Sub(int a,int b){}int Mul(int a, int b){}int Div(int a, int b){}int main(){int input,x,y;scanf("%d%d%d",&input,&x,&y);//int(*p)(int, int) = Add;int(*arr[4])(int,int) = {Add,Sub,Mul,Div};//数组arr的类型直接将函数指针的p替换即可int ret=arr[input](x,y);//使用函数指针数组对函数进行调用——转移表for (int i = 0; i < 4; i++)//函数指针数组的遍历调用{int ret = arr[i](8, 4);}return 0;}
可以使用函数指针数组对相同形参、返回类型的函数进行调用,在实际使用中实现函数的跳转功能,所以被称为转移表,有较大的使用价值。
九、函数指针数组指针
int main(){int(*p)(int, int) = Add;//p是函数指针int(*arr[4])(int,int) = {Add,Sub,Mul,Div};//arr是函数指针数组int(*(*parr))(int,int)=&arr//parr是函数指针数组指针return 0;}
写法可以按照上一级指针/数组类型模仿写。