函数指针变量,用来存放函数的
地址
。
首先看一段代码:
#include
void test()
{
printf("hehe\n");
}
int main()
{
printf("%p\n", test);
printf("%p\n", &test);
return 0;
}
✨运行结果✨
区分:
数组名——数组首元素的地址
&数组名——数组的地址
函数名——表示函数的地址
&函数名——表示的也是函数的地址
void test()
{
printf("hehe\n");
}
//下面pfun1和pfun2哪个有能力存放test函数的地址?
void (*pfun1)();
void *pfun2();
首先,能给存储地址,就要求pfun1或者pfun2是指针,那哪个是指针?
答案是:
pfun1可以存放。pfun1先和*结合,说明pfun1是指针,指针指向的是一个函数,指向的函数无参数,返回值类型为void。
练习:
#include
int Add(int x, int y)
{
return x + y;
}
int main()
{
printf("%p\n", &Add);
printf("%p\n", Add);
int a = 10;//类型为int
int arr[10] = { 0 };//数组类型为int [10]
//数组指针类型为int(*)[10]
int(*parr)[10] = &arr;//parr就是数组指针变量
//函数指针类型为int(*)(int, int)
int (*pf)(int, int) = &Add;
//pf是用来存放函数的地址 - pf就是函数指针变量
return 0;
}
数组类型为int [10]
数组指针类型为int( * )[10]
函数指针类型为int( * )(int, int)
函数解引用操作
#include
int Add(int x, int y)
{
return x + y;
}
int main()
{
int (*pf)(int, int) = &Add;
int ret = Add(2, 3);
printf("%d\n", ret);
ret = (*pf)(4, 5);
printf("%d\n", ret);
return 0;
}
#include
int Add(int x, int y)
{
return x + y;
}
int main()
{
int (*pf)(int, int) = Add;//此时的pf==Add
int ret = pf(4, 5);
//ret = (*pf)(4, 5);这里的*可以不写,也可以写很多个
//此时的*在语法上没有任何意义,只是摆设
printf("%d\n", ret);
return 0;
}
//代码1
(*( void (*)() ) 0)();
代码是一次函数调用。
解析:
(1) void (* )()为函数指针,指向的函数无需传参,且返回类型为void,即无需返回值。
(2) 代码中把 0 强制类型转换为为 void (*)() ,即一个函数的地址。
(3) 解引用地址,调用此地址处的函数,被调用的函数是无参的。
//代码2
void (*signal(int , void(*)(int)))(int);
这个代码是一次函数声明。
解析:
(1)声明的函数名signal
(2)signal函数有 2 个参数,第一个是 int 类型,第二个是void(* )(int)的函数指针类型。
(3)signal函数的返回类型依然是:void( * )(int)的函数指针类型。
如何简化代码2:
typedef void(*pfun_t)(int);
pfun_t signal(int, pfun_t);
关键字 typedef(typedef 顾名思义是类型定义,这里理解为类型重命名。)
数组是一个存放相同类型数据的存储空间,那我们已经学习了指针数组,
比如:
int *arr[10];
//arr为整型指针的数组,数组的每个元素是int*
把函数的地址存到一个数组中,那这个数组就叫函数指针数组。
那函数指针的数组如何定义呢?
int (*parr1[10])();
int *parr2[10]();
int (*)() parr3[10];
答案是:parr1
parr1 先和 [] 结合,说明parr1是数组。
数组的内容是什么呢?
是 int (*)() 类型的函数指针。
#include
int Add(int x, int y)//int (*)(int, int)
{
return x + y;
}
int Sub(int x, int y)//int (*)(int, int)
{
return x - y;
}
int Mul(int x, int y)//int (*)(int, int)
{
return x * y;
}
int Div(int x, int y)//int (*)(int, int)
{
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;
//函数指针数组 - 存放函数指针的数组
//pfArr就是一个函数指针的数组
int (* pfArr[4])(int, int) = {Add, Sub, Mul, Div};
return 0;
}
函数指针数组的用途:转移表
例子:(计算器)
#define _CRT_SECURE_NO_WARNINGS 1
#include
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;
do
{
int x = 0;
int y = 0;
int ret = 0;
menu();
printf("请选择:>");
scanf("%d", &input);
switch (input)
{
case 1:
printf("输入操作数:");
scanf("%d %d", &x, &y);
ret = Add(x, y);
printf("ret = %d\n", ret);
break;
case 2:
printf("输入操作数:");
scanf("%d %d", &x, &y);
ret = Sub(x, y);
printf("ret = %d\n", ret);
break;
case 3:
printf("输入操作数:");
scanf("%d %d", &x, &y);
ret = Mul(x, y);
printf("ret = %d\n", ret);
break;
case 4:
printf("输入操作数:");
scanf("%d %d", &x, &y);
ret = Div(x, y);
printf("ret = %d\n", ret);
break;
case 0:
printf("退出程序\n");
break;
default:
printf("选择错误\n");
break;
}
} while (input);
return 0;
}
使用函数指针数组的实现:
方法一:
#define _CRT_SECURE_NO_WARNINGS 1
#include
void menu()
{
printf("**********************************\n");
printf("******* 1.add 2. sub ******\n");
printf("******* 3.mul 4. div ******\n");
printf("******* 0.exit ******\n");
printf("**********************************\n");
//x&y x|y x>>y x<
}
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;
do
{
int x = 0;
int y = 0;
int ret = 0;
menu();
printf("请选择:>");
scanf("%d", &input);
//转移表 - 《C和指针》
int (*pfArr[5])(int, int) = {0, Add, Sub, Mul, Div};
//0 1 2 3 4
if (input == 0)
{
printf("退出计算器\n");
}
else if(input>=1 && input<=4)
{
printf("请输入2个操作数:>");
scanf("%d %d", &x, &y);
ret = pfArr[input](x, y);
printf("%d\n", ret);
}
else
{
printf("选择错误\n");
}
} while (input);
return 0;
}
方法二:借用回调函数
#define _CRT_SECURE_NO_WARNINGS 1
#include
void menu()
{
printf("**********************************\n");
printf("******* 1.add 2. sub ******\n");
printf("******* 3.mul 4. div ******\n");
printf("******* 0.exit ******\n");
printf("**********************************\n");
//x&y x|y x>>y x<
}
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;
int y = 0;
int ret = 0;
printf("请输入2个操作数:>");
scanf("%d %d", &x, &y);
ret = pf(x, y);
printf("ret = %d\n", ret);
}
int main()
{
int input = 0;
do
{
menu();
printf("请选择:>");
scanf("%d", &input);
switch (input)
{
case 1:
Calc(Add);
break;
case 2:
Calc(Sub);
break;
case 3:
Calc(Mul);
break;
case 4:
Calc(Div);
break;
case 0:
printf("退出计算器\n");
break;
default:
printf("选择错误\n");
break;
}
} while (input);
return 0;
}
指向函数指针数组的指针是一个
指针
。
指针指向一个数组
,数组的元素都是函数指针
。
回调函数就是一个通过
函数指针调用
的函数。
#define _CRT_SECURE_NO_WARNINGS 1
#include
int Add(int x, int y)
{
return x + y;
}
int Sub(int x, int y)
{
return x - y;
}
void Calc(int(*pf)(int, int))
{
int ret = pf(3, 5);
printf("%p\n", pf);
printf("%d\n", ret);
}
int main()
{
//Calc(Add);
Calc(Sub);
printf("%p\n", Sub);
return 0;
}
qsort 函数为
库函数
,可实现多种类型元素的排序。
void qsort(void* base,
size_t num, //待排序的元素个数
size_t width, //一个元素的大小,单位是字节
int(* cmp)(const void* e1, const void* e2)
//cmp指向的是:排序时,用来比较2个元素的函数)
);
(1)注意一
int a = 10;
float f = 5.5f;
char* p = &a;//int*
//错误代码
char* p2 = &f;//float*
//错误代码
void* p3 = &a;//int*
void* p4 = &f;//float*
void* ——无具体类型的指针,能够接收任意类型的地址
(2)注意二
int a = 10;
float f = 5.5f;
int* p1 = &a;
p1++;//正确
p1 = p1 + 1;//正确
void* p3 = &a;//int*
p3++;//错误代码
p3 = p3 + 1;//错误代码
*p3;//错误代码,不能解引用
void* ——无具体类型的指针
能够接收任意类型的地址
缺点:不能进行运算,不能±整数,不能解引用
排序整型数组
#include
#include
//比较2个整型函数
int cmp_int(const void* e1, const void*e2)
{
return *(int*)e1 - *(int*)e2;//升序,若为降序,只需将e1和e2位置互换
}
void print_arr(int arr[], int sz)
{
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
}
void test1()
{
int arr[] = { 9,8,7,6,5,4,3,2,1,0 };
int sz = sizeof(arr) / sizeof(arr[0]);
qsort(arr, sz, sizeof(arr[0]), cmp_int);
//打印
print_arr(arr, sz);
}
int main()
{
//测试排序整型数组
test1();
return 0;
}
排序结构体数据
#include
#include
#include
struct Stu
{
char name[20];
int age;
};
int cmp_by_name(const void*e1, const void*e2)
{
return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name);
//strcmp是比较两个字符串大小的库函数
}
int cmp_by_age(const void* e1, const void* e2)
{
return ((struct Stu*)e1)->age -((struct Stu*)e2)->age;
}
void test2()
{
struct Stu s[3] = { {"zhangsan", 15}, {"lisi", 30},{"wangwu", 10}};
int sz = sizeof(s) / sizeof(s[0]);
//按照名字排序
qsort(s, sz, sizeof(s[0]), cmp_by_name);
//按照年龄来排序
//qsort(s, sz, sizeof(s[0]), cmp_by_age);
}
int main()
{
//排序结构体数据
test2();
return 0;
}
设计一个自定义的冒泡排序函数,使得其作用和 qsort 一样,可以排序不同类型的数据
#include
//比较2个整型函数
int cmp_int(const void* e1, const void*e2)
{
return *(int*)e1 - *(int*)e2;
}
void Swap(char* buf1, char* buf2, int width)
{
int i = 0;
for (i = 0; i < width; i++)
{
char tmp = *buf1;
*buf1 = *buf2;
*buf2 = tmp;
buf1++;
buf2++;
}
}
//使用回调函数实现一个通用的冒泡排序函数
void BubbleSort(void* base, size_t num, size_t width, int (*cmp)(const void*, const void*))
{
size_t i = 0;//使得比较时num和i都是无符号整型unsigned int
//趟数
for (i = 0; i < num - 1; i++)
{
//比较的对数
size_t j = 0;
for (j = 0; j < num - 1 - i; j++)
{
//base[j] ==> *(base+j)
if (cmp((char*)base+j*width, (char*)base+(j+1)*width)>0)
{
//交换
Swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
}
}
}
}
void print_arr(int arr[], int sz)
{
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
}
//测试自定义的BubbleSort();
void test3()
{
int arr[] = { 9,8,7,6,5,4,3,2,1,0 };
int sz = sizeof(arr) / sizeof(arr[0]);
BubbleSort(arr, sz, sizeof(arr[0]), cmp_int);
//打印
print_arr(arr, sz);
}
int main()
{
//测试自定义的BubbleSort();
test3();
return 0;
}
如有问题,欢迎批评指正呀!