17.0、C语言——指针详解(3)
来看一个C语句【该语句出自《C陷阱与缺陷》这本书】:
int main() {
(*(void(*)())0)();
return 0;
}
我们先把这段代码拆开来看 void(*)() 这其实是一个函数指针类型,该函数返回值为void,参数为无参,那么( void(*)() ) 0 表示把 0 强制类型转换为 void(*)() 类型,之后再解引用符 * ,表示获取该函数,最后一个括号表示调用该函数
总结:把 0 强制类型转换成一个 函数指针类型,该函数指针指向的函数是一个无参无返回值类型的函数,然后当 0 变成一个指针类型之后对他进行解引用操作,去调用以 0 为地址处的该函数
再来看第二个C语句:
int main() {
void(*signal(int, void(*)(int)))(int);
return 0;
}
还是一样把这个语句拆开来看,signal( int, void(*)(int) ) 是一个函数,该函数有两个参数,第一个参数类型为 int,第二个参数类型为函数指针,把函数名和参数去掉后剩下的void(*)(int)就是signal函数的返回类型~
总结:signal是一个函数声明;signal函数的参数有2个,第一个是int。第二个是函数指针,该函数指针指向的函数的参数是int,返回类型是void;signal函数的返回类型也是一个函数指针:该函数指针指向的函数的参数是int,返回类型是void
函数指针 typedef 重命名;向上面那个代码很复杂我们来简化一下:
int main() {
//简化 重命名为 pfun_t
typedef void(*pfun_t)(int);
pfun_t signal(int,pfun_t);
return 0;
}
当我们 解引用符函数指针去调用函数的时候可以发现,不管我们用不用解引用符 都可以正常的调用到函数,代码如下所示:
int add(int a,int b) {
return a + b;
}
int main() {
int (*padd)(int, int) = add;
printf("%d", padd(1, 2));
printf("%d", (*padd)(1, 2));
return 0;
}
函数指针数组:
定义方式:int ( * parr1 [ 10 ] ) ();【这里 parr1 先与[ ] 结合成为数组,数组元素的类型是int(*)() 类型的函数指针】
定义的方式和指针差不多就不过多介绍了,直接上代码:
int add(int a,int b) {return a + b;}
int sub(int a,int b) {return a - b;}
int mul(int a,int b) {return a * b;}
int div(int a,int b) {return a / b;}
int main() {
int(*parr[10])(int, int) = {add,sub,mul,div};
//函数调用
int i = 0;
for (i = 0;i<4;i++) {
printf("%d\n",parr[i](3,2));
}
return 0;
}
函数指针数组的用途:转移表
例子:(简易计算器)
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int add(int a,int b) {return a + b;}
int sub(int a,int b) {return a - b;}
int mul(int a,int b) {return a * b;}
int div(int a,int b) {return a / b;}
int main() {
int input = 0;
int x = 0;
int y = 0;
int (*p[])(int, int) = {0,add,sub,mul,div};
int arrLength = sizeof(p) / sizeof(p[1]);
do {
printf("请选择>\n");
printf("***** 1.add 2.sub 3.mul 4.div *****\n");
scanf("%d",&input);
if (input >= 1 && input < arrLength) {
printf("请输入两个操作数\n");
scanf("%d%d", &x, &y);
int ret = p[input](x, y);
printf("result = %d\n", ret);
}
else if (input == 0) {
printf("退出");
}
else {
printf("输入的数字有误\n");
}
} while (input);
return 0;
}
回调函数:
回调函数就是一个通过函数指针调用的函数,如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。
回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用,用于对该事件或条件进行相应。
还是上面的计算器的例子(这里用回调函数的方式实现):
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int add(int a,int b) {return a + b;}
int sub(int a,int b) {return a - b;}
int mul(int a,int b) {return a * b;}
int div(int a,int b) {return a / b;}
int Calc(int (*p)(int,int)) {
int x = 0;
int y = 0;
printf("请输入两个操作数\n");
scanf("%d%d",&x,&y);
return p(x, y);
}
int main() {
int input = 0;
int ret = 0;
do {
printf("请选择>\n");
printf("***** 1.add 2.sub 3.mul 4.div *****\n");
scanf("%d",&input);
switch (input)
{
case 1:
printf("result = %d\n", Calc(add));
break;
case 2:
printf("result = %d\n", Calc(sub));
break;
case 3:
printf("result = %d\n", Calc(mul));
break;
case 4:
printf("result = %d\n", Calc(div));
break;
case 0:
printf("退出");
break;
default:
printf("输入有误\n");
break;
}
} while (input);
return 0;
}
回调函数可以很好的解决代码冗余的问题
指向函数指针数组的指针:
指向函数指针数组的指针是一个 指针 ,指针指向一个 数组,数组的元素都是 函数指针
代码如下:
int main() {
int arr[10] = { 0 };
int(*p)[10] = &arr;//取出数组的地址
int(*p2)(int,int);//函数指针
int(*parr[10])(int,int);//parr是一个数组-函数指针的数组
int(*(*pparr)[10])(int,int) = &parr;//pparr是一个指向[函数指针数组]的指针
//pparr是一个数组指针,指针指向的数组有10个元素
//指向的数组的每个元素的类型是一个函数指针int(*)(int,int)
return 0;
}
int ( * ( *pparr ) [10] )(int,int) = &parr;
* 先与 pparr 结合说明这是一个指针,然后指向 [ ] 数组,把 ( *pparr ) [10] 去掉之后就是指向的数组的每个元素的类型 -> int ( * )(int,int)
qsort()库函数:
void qsort(void* base,
size_t num,
size_t width,
int(*cmp)(const void* e1,const void* e2));
第一个参数是:需要比较的数组 地址;
第二个参数是:数组中的元素个数;
第三个参数是:每个元素占用空间的大小;
第四个参数是:用来比较两个元素的函数地址【函数中的参数是传递两个比较元素的地址】;
【void :表示没有规定特定类型的参数,所以可以接收任意类型的参数】
我们之前说到过,指针类型决定了 解引用符能够访问的内存空间,但是 void* 类型的指针由于没有指定指针类型所以不能够 进行 解引用符操作~【不然会报错 非法的间接寻址】
当然 指针 也不能进行 + - 整数的操作,因为+1 , -1 也需要指定指针的类型,不然也不知道要跳过多少字节
在qsort()库函数中规定:
当第一个元素小于第二个元素的时候,让比较函数返回 负数
当第一个元素等于第二个元素的时候,让比较函数返回 0
当第一个元素大于第二个元素的时候,让比较函数返回 正数
实例代码如下所示【在调用qsort()库函数的时候记得引头文件:#include<stdlib.h>】:
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
int com_int(const void* e1, const void* e2) {
//这里由于void类型的指针不能进行解引用符操作,
//所以先将他们强制类型转换为int*类型再进行解引用符操作
return *(int*)e1 - *(int*)e2;
}
int main() {
int arr[10] = { 9,8,7,6,5,4,3,2,1,0 };
int sz = sizeof(arr) / sizeof(arr[0]);
qsort(arr, sz, sizeof(arr[0]), com_int);
int i = 0;
for (i = 0; i < sz; i++) {
printf("%d\n", arr[i]);
}
return 0;
}
输入结果为 0 1 2 3 4 5 6 7 8 9 ,排序成功~
【注意:元素比较的函数返回值类型规定必须是整数】
如果比较的是一个结构体的数组:
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<String.h>
struct Stu
{
char name;
int age;
};
int cmp_stu_by_age(const void* e1,const void* e2) {
return ((struct Stu*)e1)->age - ((struct Stu*)e1)->age;
}
int cmp_stu_by_name(const void* e1, const void* e2) {
return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e1)->name);
}
int main() {
struct Stu s[3] = { {"xiaoming",20},{"xiaolan",18}, {"xiaohong",26}};
int sz = sizeof(s) / sizeof(s[0]);
qsort(s, sz, sizeof(s[0]), cmp_stu_by_age);
qsort(s, sz, sizeof(s[0]), cmp_stu_by_name);
return 0;
}
【这里注意如果比较的是字符串的大小要用 strcmp()库函数去比较】