前言
在上篇指针进阶中,我们对字符指针、指针数组、数组指针以及数组传参和指针传参有了一定的了解,你以为指针进阶就只有这些内容嘛?不不不,接下来,小羊将继续完善指针进阶内容,坐好小板凳准备上课了~~~
一、函数指针
1.1 函数指针的定义
函数指针,顾名思义,就是一个指向函数的指针
上篇中我们学到
整形指针是接收整形的地址
字符指针是接收字符的地址
数组指针是接收数组的地址
那么函数有地址吗?函数名又表示什么呢?
答案是:函数是有地址的,地址是函数名或者&函数名,解引用时,p和*p都可以
#include<stdio.h> int Add(int x, int y) { return x + y; } int main() { int a = 1, b = 2; int c = Add(a, b); printf("%d\n", c); printf("%p\n", &Add); printf("%p\n", &Add); return 0; }
运行结果:
3 00007FF750191348 00007FF750191348
由此可见,函数名也可以表示函数的地址
所以问题来了,我们以数组指针为例:
写一个指向int arr[10]数组的数组指针
第一步: (*p) //先确定是一个指针 第二步: (*p)[10] //确定指向的是一个有10个元素的数组 第三步: int(*p)[10] //确定该数组元素为int型 第四步: int(*p)[10]=&arr;//将数组的地址赋值给数组指针 //或者int(*p)[10]=arr
我们照着数组指针的例子来写一个函数指针:指向int add(int x,int y)
第一步: (*p) //先确定是一个指针 第二步: (*p)(int,int) //确定指向的函数有两个参数 第三步: int (*p)(int,int) //确定该函数的返回类型 第四步: int (*p)(int,int)=&add;//将函数的地址赋值给函数指针 //等价于:int (*p)(int,int)=add;
1.2 函数指针调用
1.1中得到 &函数名==函数名 ,所以函数指针的解引用调用可以不写*,也可以不写*。
既然我们现在知道函数指针是怎么写的,那么函数指针有什么用呢?
数组指针可以用来访问数组,那函数指针当然也就是调用函数的了,
我们还是以数组指针为例:
#include<stdio.h> int main() { int arr[10] = { 1,2,3,4,5 }; int(*p)[10] = arr; for (int i = 0; i < 5; i++) { printf("%d ", (*p)[i]); } printf("\n"); return 0; }
那现在用函数指针调用函数:
#include<stdio.h> int Add(int x, int y) { return x + y; } int main() { int a = 1, b = 2; int(*p1)(int, int) = &Add; int(*p2)(int, int) = Add; int tmp1 = Add(a, b); int tmp2 = (*p1)(a, b);//写法一 int tmp3 = (p2)(a, b);//写法二 printf("tmp1=%d\ntmp2=%d\ntmp3=%d", tmp1, tmp2, tmp3); return 0; }
运行结果:
tmp1=3 tmp2=3 tmp3=3
1.3 有趣的代码
一、
(*(void(*)())0)(); //先分解 一、void(*)()是一个无参无返回类型的函数指针 二、(void(*)())0是将0强制类型转换,0原本是int类型,被强制转换为void(*)()函数指针 三、*(void(*)()0)这是将0转换为函数指针后的解引用操作 四、(*(void(*)()0)()此时0就是一个函数的地址,可以看成(*0)(),意思就是解引用一个函数地址并且调用
二、
void(*signal(int,void(*)(int)))(int); 我先一步一步分解给你们看 void(*)(int) signal(int,void(*)(int)) signal(int,void(*)(int)) void(*signal(int,void(*)(int)))(int) signal先和(int,void(*)(int))结合,说明它是一个函数,参数为int,void(*)(int),现在我们明确了它的函数名和参数,还需要知道它的返回类型,先看一个函数,int Add(int),这个函数的函数名是Add,参数是int,把函数名和参数去掉后就是返回类型,同样,把这里的函数名和参数去掉,void(*)(int)这个就是它的返回类型
上面代码是一个函数调用
二、函数指针数组
2.1 函数指针数组的定义
函数指针数组,存放函数指针的数组,每一个元素都是函数指针类型
先写出函数指针 int(*p)(int,int) 改成数组 int(*p[10])(int,int)
例:
#include<stdio.h> 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; } int main() { int(*pf1) (int, int) = Add; int(*pf2) (int, int) = Sub; int(*pf3) (int, int) = Mul; int(*pf4) (int, int) = Div; int (*arr[4])(int, int) = { Add,Sub,Mul,Div }; int sz = sizeof(arr) / sizeof(arr[0]); for (int i = 0; i < sz; i++) { printf("%d\n", arr[i](7, 4)); } return 0; }
2.2 实战操作
用C语言制作简易计算器
#include<stdio.h> void menu() { printf("****************************\n"); printf("***** 1.Add 2.Sub *****\n"); printf("***** 3.Mul 4.Div *****\n"); printf("***** 0.exit *****\n"); printf("****************************\n"); } 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; } int main() { int input = 0; int x = 0, y = 0; int (*arr[5])(int, int) = { 0,Add,Sub,Mul,Div }; do { menu(); printf("请选择:>"); scanf("%d", &input); if (input >= 1 && input <= 4) { printf("请输入两个操作数:>"); scanf("%d %d", &x, &y); printf("%d\n", arr[input](x, y)); } else if (input == 0) { printf("退出计算机"); break; } else printf("选择错误\n请重新选择:>\n"); } while (input); return 0; }
三、函数指针数组指针
指向函数指针数组的指针,是一个指针,指针指向存放函数指针的数组,该数组成员都是函数指针
先写出函数指针 int (*p)(int,int) 改写成函数指针数组 int(*p[10])(int,int) 最后写成函数指针数组指针 int(*(*p)[10]))(int,int)
示例:
#include<stdio.h> void menu() { printf("****************************\n"); printf("***** 1.Add 2.Sub *****\n"); printf("***** 3.Mul 4.Div *****\n"); printf("***** 0.exit *****\n"); printf("****************************\n"); } 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; } int main() { int (*pa)(int, int) = Add;//函数指针 int (*arr[4])(int, int) = { Add };//函数指针数组,存放函数指针的数组 int (*(*ppa)[4])(int, int) = &arr;//函数指针数组指针,存放函数指针数组地址的指针 return 0; }
四、回调函数
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
示例:
#include<stdio.h> void menu() { printf("****************************\n"); printf("***** 1.Add 2.Sub *****\n"); printf("***** 3.Mul 4.Div *****\n"); printf("***** 0.exit *****\n"); printf("****************************\n"); } 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 Calc(int (*pf)(int, int)) { int x = 0, y = 0; printf("请输入两个操作数:>"); scanf("%d %d", &x, &y); int ret = pf(x, y); printf("%d\n", ret); } int main() { int input = 0; do { menu(); printf("请选择:>"); scanf("%d", &input); switch (input) { case 0: printf("退出计算机\n"); break; case 1: Calc(Add); break; case 2: Calc(Sub); break; case 3: Calc(Mul); break; case 4: Calc(Div); break; default: printf("输入错误\n"); break; } } while (input); return 0; }
4.1 排序
冒泡排序,相邻两个两个的比较并交换位置,在C语言初阶数组中,详细讲解了关于冒泡排序的知识点,忘记了的铁汁们可以看一下
代码展示:
#include <stdio.h> void Print(int arr[], int sz) { for (int i = 0; i < sz; i++) { printf("%d ", arr[i]); } } void bubble_sort(int arr[],int sz) { int i = 0; for (i = 0; i < sz - 1; i++) { int j = 0; for (j = 0; j < sz - i - 1; j++) { if (arr[j] > arr[j + 1]) { int tmp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = tmp; } } } } int main() { int arr[] = { 5,3,7,6,1,8,9,2,4,0 }; int sz = sizeof(arr) / sizeof(arr[0]); bubble_sort(arr,sz); Print(arr, sz); return 0; }
4.2 qsort部分展示
冒泡排序只能排序整形,而qsort函数,内部采用快速排序,可以排序各种类型的数据,接下来展示qsort排序部分类型的方法.
qsort是一个库函数,快速排序的方法来实现的, 头文件是<stdlib.h>
qsort库函数,void qsort( void *base, size_t num, size_t width, int (_cdecl *compare )
(const void *elem1, const void *elem2 ) );传入的参数,一个是指针,一个整形,一个整形一个函数指针,base 数组首元素(就是数组名),num数组里有多少个元素,width每个元素的大小(单位是字节),compare比较两个指针指向的元素,小于 输出小于0的元素,等与 输出0,大于 输出大于0的元素 排序任意类型
示例:
qsort函数部分应用
分别将元素比较方法int_cmp和char_cmp的指针(地址) 传给 qsort函数.由qsort函数调用这些比较函数
#include <stdlib.h> #include <stdio.h> int int_cmp(const void* e1, const void* e2)//整形元素排序方法 { return *(int*)e1 - *(int*)e2; } int char_cmp(const void* e1, const void* e2)//字符型元素排序方法 { return *(char*)e1 - *(char*)e2; } int main() { int arr1[10] = { 4,5,1,8,9,2,10,3,7,6 }; char arr2[] = "fbadegc"; int sz1 = sizeof(arr1) / sizeof(arr1[0]); int sz2 = sizeof(arr2) / sizeof(arr2[0]); qsort(arr1,sz1,sizeof(arr1[0]),int_cmp); for (int i=0; i < sz1; i++) { printf("%d ", arr1[i]); } printf("\n"); qsort(arr2, sz2, sizeof(arr2[0]), char_cmp); for (int i = 0; i < sz2; i++) { printf("%c ", arr2[i]); } printf("%s", arr2); return 0; }
运行结果:
1 2 3 4 5 6 7 8 9 10 a b c d e f g
qsort函数用冒泡排序的模拟实现,以及各种类型的排序咱们下篇一起学习~
希望这篇文章对铁汁们有所帮助,咱们下期再见!