一.一级指针传参
void print(int* ptr, int sz)
{
int i = 0;
for (i = 0;i < sz;i++)
{
printf("%d", *(ptr + i));
}
}
void test(char* p)
{
}
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int* p = arr;
int sz = sizeof(arr) / sizeof(arr[0]);
//p是一级指针
print(p, sz);
char ch = 'w';
test(&ch);//char*
char* p1 = &ch;
test(p1);
return 0;
}
思考:
1.当我们自己设计函数的时候,一级指针传参,一级指针接受
2.当一个函数的参数部分为一级指针的时候,函数能接收一级指针和地址
二.二级指针传参
void test(int* *p2)
{
**p2 = 20;
}
int main()
{
int a = 10;
int* pa = &a;//pa是一级指针
int** ppa = &pa;//ppa是二级指针
//把二级指针进行传参
test(ppa);
test(&pa);//传一级指针变量的地址
int* arr[10] = { 0 };
test(arr);//传存放一级指针的数组
printf("%d\n", a);
return 0;
}
思考:
1.当我们自己设计函数的时候,二级指针传参,二级指针接收
2.当一个函数的参数部分为一级指针的时候,函数能接收二级指针,一级指针变量的地址,存放一级指针的数组
三.函数指针
int Add(int x, int y)
{
return x + y;
}
int main()
{
int a = 10;
int* pa = &a;
char ch = 'w';
char* pc = &ch;
int arr[10] = { 0 };
int(* parr)[10] = &arr;//取出数组的地址
//parr是指向数组的指针-存放的是数组的地址
//函数指针-存放函数地址的指针
//&函数名-取到的就是函数的地址
int(* pf)(int,int) = &Add;
//pf就是函数指针变量
printf("%p", &Add);
printf("%p", Add);
//结果一模一样 都是CF00007FF6D39213CF
//&函数名=函数名
return 0;
}
首先从名字上看,整型指针是指向整型的指针,字符指针是指向字符的指针,函数指针当然是存放函数的指针啦。
int a = 10;
int* pa = &a;
整型指针,将整型变量的地址,赋值给*pa,前面有*证明pa是指针,变量是整型,所以加上类型int
char ch = 'w';
char* pc = &ch;
字符指针,将字符变量的地址,赋值给*pc,前面有*证明pc是指针,变量是字符型,所以加上类型char
int arr[10] = { 0 };
int(* parr)[10] = &arr;
数组指针,将数组的地址,也就是取数组名的地址,赋值给*parr,数组类型是int[10],所以要加上类型,为了防止parr先和[]结合要加上括号
通过上述三个指针咱们也能推演出函数指针:
int Add(int x, int y)
{
return x + y;
}
这个是函数Add,也是我们函数指针要指向的函数
首先我们进行了打印Add的地址,和直接打印函数名Add
printf("%p", &Add);
printf("%p", Add);
结果是一样的,证明 函数名==&函数名
int(* pf)(int,int) = &Add;
函数指针,取函数Add的地址,赋值给*pf,前面有*证明pc是指针,函数的返回类型是int,参数类型是int,int,所以将int(int,int)加上,为防止pf()结合,要加上括号
void test(char* str)
{
}
int main()
{
//?= &test;
return 0;
}
现在练习一下,将函数test的函数指针写出来,答案在下面
-----------------------------------------------------------------------------
-----------------------------------------------------------------------------
-----------------------------------------------------------------------------
-----------------------------------------------------------------------------
-----------------------------------------------------------------------------
-----------------------------------------------------------------------------
答案是:void(* pt)(char*) = &test
你做对了吗
接下来咱们继续对函数指针进行讨论
int Add(int x, int y)
{
return x + y;
}
int main()
{
//int(*pf)(int, int) = &Add;
int(*pf)(int, int) = Add;//Add==pf
//int ret=(*pf)(3, 5);//1
//int ret = Add(3, 5);//2
int ret = pf(3, 5);//3
printf("%d\n", ret);
return 0;
}
这段代码里面实现了通过函数指针调用函数的三种方法
第一种:
int ret=(*pf)(3, 5)
函数指针是pf,咱们解引用他,*pf,后面再加上要传的参数(3,5),为防止pf先和()结合,所以要用括号把*pf括住,在用整型ret接收返回值
第二种:
int ret = Add(3, 5)
因为函数名==&函数名,所以函数指针可以写为int(*pf)(int, int) = Add,这句代码就可以看出,pf==Add,指针变量里面存的就是Add,所以解引用可以变成Add
第三种:
int ret = pf(3, 5)
因为pf=Add,所以Add可以替换成pf
综上三种,其实(*pf)(3, 5)里面的*本身是没有用的,只是为了好理解,但是只有函数指针是这样子
,其他指针没有这种情况
接下来咱们看两个有趣的问题
(*(void(*)())0)() 和void(*signal(int, void(*)(int)))(int)
下面是简单的理解,具体的请去看(1条消息) 学习函数指针时遇到的两个问题_Любовь всей жизни50的博客-CSDN博客
(*(void(*)())0)()
分析
0先与前面的括号结合,()0,强制类型转换为(void(*)())
void(*)()的地址是0,void(*)()是函数指针类型
前面加*(void(*)()),解引用,就是把这个函数拿出来(函数)()就是调用这个函数
void(*signal(int,void(*)(int)))(int)
signal是函数名,后面括号里的int,void(*)(int)是参数,void(*)(int)是函数指针指向,返回类型为void,参数为int的函数
对于函数还有一个函数返回类型,将signal(int,void(*)(int)去掉,void(*)(int),是一个函数指针类型
signal的返回类型也是一个函数指针,指向一个参数为int,返回类型为void的函数
signal就是一个函数声明
四.函数指针数组
什么是函数指针数组呢?整型数组,是存放整型的数组,函数指针数组当然是存放同类型函数指针的数组啦
int Add(int x, int y)
{
return x + y;
}
int Sub(int x, int y)
{
return x - y;
}
int main()
{
int(*pf1)(int, int) = Add;
int(*pf2)(int, int) = Sub;
int(*pfArr[2])(int, int)={Add,Sub};//函数指针数组
return 0;
}
如上述代码所示,pfArr就是函数指针数组啦
那么这个函数指针数组有什么应用呢
---------------------------------------------------------------------------------------------------------------------------------
有一个题目,写一个代码,实现计算器-+-*/
代码如下
int Add(int x, int y)
{
return x + y;
}
int Sub(int x, int y)
{
return x - y;
}
int Mul(int x, int y)
{
return x - y;
}
int Div(int x, int y)
{
return x - y;
}
void menu()
{
printf("*************************\n");
printf("*******1.add 2.sub******\n");
printf("*******3.mul 4.div******\n");
printf("******* 0.exit ******\n");
}
int main()
{
int input = 0;
do {
menu();
int x = 0;
int y = 0;
int ret = 0;
printf("请选择:>");
scanf("%d", &input);
switch (input)
{
case 1:
printf("请输入两个操作数>:\n");
scanf("%d %d", &x, &y);
ret=Add(x, y);
break;
case 2:
printf("请输入两个操作数>:\n");
scanf("%d %d", &x, &y);
ret=Sub(x, y);
break;
case 3:
printf("请输入两个操作数>:\n");
scanf("%d %d", &x, &y);
ret=Mul(x, y);
break;
case 4:
printf("请输入两个操作数>:\n");
scanf("%d %d", &x, &y);
ret=Div(x, y);
break;
case 0:
printf("退出程序\n");
break;
default:
printf("选择错误,重新选择\n");
break;
}
printf("ret=%d\n", ret);
} while (input);
return 0;
}
这个代码很容易看懂,但是太过于冗余,而且有很多重复代码,那么咱们有没有办法把它简化一下呢?
当然有,并且咱们要用到函数指针数组的知识
int Add(int x, int y)
{
return x + y;
}
int Sub(int x, int y)
{
return x - y;
}
int Mul(int x, int y)
{
return x - y;
}
int Div(int x, int y)
{
return x - y;
}
void menu()
{
printf("*************************\n");
printf("*******1.add 2.sub******\n");
printf("*******3.mul 4.div******\n");
printf("******* 0.exit ******\n");
}
int main()
{
int input = 0;
do {
menu();
int x = 0;
int y = 0;
int ret = 0;
printf("请选择:>");
scanf("%d", &input);
int (*pfArr[5])(int, int) = {NULL,Add,Sub,Mul,Div };
if (input > 0 && input < 5)
{
printf("请输入两个操作数>:\n");
scanf("%d %d", &x, &y);
ret=pfArr[input](x, y);
printf("ret=%d\n", ret);
}
else if (input == 0)
{
printf("退出程序\n");
}
else
{
printf("输入错误,请重新输入\n");
}
} while (input);
return 0;
}
首先,咱们肯定要使用函数指针数组,原本程序(最开始的那个代码)里面的+-*/四个函数,类型相同所以可以存在函数指针数组
int (*pfArr[ ])(int, int) = {Add,Sub,Mul,Div }; 这就是一个函数指针数组啦,但是我们还没有把[]里面的数字填上,按道理应该填4呀,一共四个函数嘛,可是咱们看函数menu,1是选择add,2是sub,3是mul,4是div,每个选择的数字都比下标多1呀,那怎么办呢
咱们在Add之前,再加一个函数指针,加一个NULL空指针
int (*pfArr[5])(int, int) = {NULL,Add,Sub,Mul,Div };这样就很妙啦
pfArr[input](x, y)就可以调用咱们需要的函数
再通过if else语句就可以实现计算器啦,不需要使用switch语句
这个函数指针数组起到了一个跳板的作用,我们经常把这样一个数组称为转移表----《C和指针》
五.指向函数指针数组的指针
函数指针数组是数组,数组就有地址,那么就可以&函数指针数组
那么这个指针该怎么表示呢
整型数组,int arr[5] ----------- int(*p1)[5]=&arr
整型指针的数组,int* arr[5] ----------- int* (*p2)[5]=&arr
所以 int(*p)(int,int) ------------ int(* p3[4])(int,int) -------------- int(* (*p4)[4])(int,int)=&p3
六.回调函数
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外一方调用的,用于该事件或条件进行响应。
这是官方的概念,比如有一个A函数和一个B函数,把A函数的地址传递给B函数,A函数的形参部分就是A函数的指针,我们进入B函数时,B函数通过A函数的指针调用A函数。
接下来我们通过改造一下,最开始的计算器代码,来解释一下:
int Add(int x, int y)
{
return x + y;
}
int Sub(int x, int y)
{
return x - y;
}
int Mul(int x, int y)
{
return x - y;
}
int Div(int x, int y)
{
return x - y;
}
void menu()
{
printf("*************************\n");
printf("*******1.add 2.sub******\n");
printf("*******3.mul 4.div******\n");
printf("******* 0.exit ******\n");
}
int Calc(int(*pf)(int, int))
{
int x = 0;
int y = 0;
printf("请输入两个操作数>:");
scanf("%d %d", &x, &y);
return pf(x, y);
}
int main()
{
int input = 0;
do {
menu();
int ret = 0;
printf("请选择:>");
scanf("%d", &input);
switch (input)
{
case 1:
Calc(Add);
ret = Calc(Add);
break;
case 2:
Calc(Sub);
ret = Calc(Sub);
break;
case 3:
Calc(Mul);
ret = Calc(Mul);
break;
case 4:
Calc(Div);
ret = Calc(Div);
break;
case 0:
printf("退出程序\n");
break;
default:
printf("选择错误,重新选择\n");
break;
}
printf("ret=%d\n", ret);
} while (input);
return 0;
}
如上述代码,咱们没有直接调用Add等函数而是通过函数Calc来间接使用,这里的参数我们用的时函数指针,通过函数指针调用函数。
七.qsort函数
(2条消息) 对库函数qsort的理解_Любовь всей жизни50的博客-CSDN博客
这个前几天写的一个博客里面就有写到,感兴趣的同志们可以去看一下