一、数组名和&数组名
二、字符指针
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、&数组名,表示取出的是这个数组。
#include
int main()
{
char arr[5] = "abc";
char* p = arr;
printf("%s", p);//打印abc
return 0;
}
数组名是首元素的地址,这里指针p指向的是arr数组的首元素'a'的地址,可以通过指针p,以%s的形式打印字符串。
#include
int main()
{
const char* p = "abc";//字符串abc为常量字符串
printf("%s", p);
return 0;
}
因为"abc"是常量字符串,不能被修改,所以指针p需要使用const修饰。若没有const,.c文件运行时程序会直接挂掉,.cpp文件更加严格,会直接报错。
指针数组本质上是数组,是存放指针的数组。
#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,数组指针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]。
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);
}
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);//传过来的相当于第一行的地址
}
void text(int* p)
{}
int main()
{
int a = 0;
int* pa = &a;
int arr[10] = { 0 };
text(&a);//可以传整型变量的地址
text(pa);//可以传一级指针的地址
text(arr);//可以传整型数组的地址
return 0;
}
只要传过来的本质是一级指针,就可以用一级指针接收。
void text(int** p)
{}
int main()
{
int* p1 = NULL;
int** p2 = NULL;
int* arr[10];
text(&p1);//可以传一级指针的地址
text(p2);//可以传二级指针
text(arr);//可以传指针数组
return 0;
}
对于函数名与&函数名,它们的地址是一样的,且本质上没有任何区别。
使用函数值指针进行函数的调用,可以把p当成函数名调用,解引用无意义。
( *( void (*)() )0 )();
以上代码是一次函数调用,先将0强制类型转换为函数指针类型,该函数指针无参,返回值为void。由于函数指针解引用无意义,所以这里的这颗*可有可无。整个代码的意思是对0地址处的函数进行一次函数调用。
void ( *signal( int , void(*)(int) ) )(int);
以上代码是一次函数声明。
signal是函数名,它的参数是int和函数指针void(*)(int),返回类型是函数指针void(*)(int)。
typedef void(*pf_t)(int);
pf_t signal(int,pf_t);
把void(*)(int)类型重命名为pf_t。
上方函数可以用pf_t来代替void(*)(int)。
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;
}
写法可以按照上一级指针/数组类型模仿写。