- int main() {
- (*(void(*)())0)();
- return 0;
- }
我们先把这段代码拆开来看 void(*)() 这其实是一个函数指针类型,该函数返回值为void,参数为无参,那么( void(*)() ) 0 表示把 0 强制类型转换为 void(*)() 类型,之后再解引用符 * ,表示获取该函数,最后一个括号表示调用该函数
总结:把 0 强制类型转换成一个 函数指针类型,该函数指针指向的函数是一个无参无返回值类型的函数,然后当 0 变成一个指针类型之后对他进行解引用操作,去调用以 0 为地址处的该函数
- 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
- 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
- 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
- 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)
- 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
- #define _CRT_SECURE_NO_WARNINGS 1
- #include
- #include
- 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
- #include
- #include
- 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()库函数去比较】