目录
1.函数指针数组
在学习函数指针数组之前,我们先类比一下:
整型数组:存放整型的数组;
字符数组:存放字符的数组:
指针数值:存放地址的数组......
那函数指针数组:存放函数指针的数组?答案是:对的
函数指针数组是指针数组中的一种,这种数组中存放的函数指针(函数的地址),并且存放在函数指针数组中的元素类型要相同。
我们假设存在四种函数,函数的类型相同,分别是Add,Sub,Mul,Div.现在如果想将这四种函数放在一个数组中,我们该怎么做?接下来我们看看具体做法:
由于函数指针数组中存放的是函数的地址,所以我们需要创建一个指针变量:
int (* parr[ 4])(int,int )={ Add,Sub,Mul,Div }; parr为函数指针数组的名字,int (*)(int,int)为数组元素类型,parr[4]中的4表示数组中由4个元素。使用函数指针数组,我们可以通过数组下标找到函数。接下来我们画图来理解一下:
2.函数指针数组举例——转移表
在练习转移表的代码之前,我们先来看看一般情况下的代码该怎么写:
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;
int ret = 0;
do {
menu();
int x = 0, y = 0;
printf("请输入>>");
scanf("%d", &input);
switch (input)
{
case 1:
printf("请输入两个数>>");
scanf("%d %d", &x, &y);
ret = add(x, y);
printf("%d\n", ret);
break;
case 2:
printf("请输入两个数>>");
scanf("%d %d", &x, &y);
ret = sub(x, y);
printf("%d\n", ret);
break;
case 3:
printf("请输入两个数>>");
scanf("%d %d", &x, &y);
ret = mul(x, y);
printf("%d\n", ret);
break;
case 4:
printf("请输入两个数>>");
scanf("%d %d", &x, &y);
ret = div(x, y);
printf("%d\n", ret);
break;
case 0:
printf("退出计算器\n");
break;
default:
break;
}
} while (input);
return 0;
}
上述代码中的每个case语句中都会出现几句相同的语句,我们是否可以将这几个重复出现的代码进行简化,当然是可以了。
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 ret = 0;
int x=0, y = 0;
int input = 0;
do {
menu();
printf("请输入>>");
scanf("%d", &input);
if (input == 0)
{
printf("退出计算器\n");
}
else if (input > 0 && input <= 4)
{
int (*p[5])(int, int) = { NULL,add,sub,mul,div };//转移表
printf("请输入两个整数>>");
scanf("%d %d", &x, &y);
ret = p[input](x, y);
printf("%d\n", ret);
}
else
{
printf("输入错误,请重新输入\n");
}
} while (input);
return 0;
}
3.回调函数
不知道是否有小伙伴会有疑问,回调函数是什么?好像没有听过这个词语。OK,那让我来介绍一下回调函数。
回调函数就是一个通过函数指针调用的函数。(这句话应该会有很多小伙伴依然不理解)简单来说就是把一个函数的指针(函数指针中存放的是函数的地址,也就是函数的地址)作为参数传递给另一个函数 ,前面那个函数就被成为回调函数。
接下来,我们通过代码来看一下:
int add(int x, int y)
{
return x + y;
}
void test(int (*pa)(int, int))
{
int x = 0, y = 0;
scanf("%d %d", &x, &y);
int ret = pa(x, y);
printf("%d\n", ret);
}
int main()
{
test(add);
return 0;
}
在这个函数中,我们将add这个加法函数的地址作为参数传递给test函数,然后我们在test函数中通过指针调用add函数(在上面代码中,add就是回调函数)。
注意:回调函数是被动调用,也就是在其他函数中被调用,也由这个函数传参
既然已经学习了回调函数,那我们是不是可以通过回调函数对刚才的代码进行改进:
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");
}
void calc(int(*p)(int, int))
{
int x = 0, y = 0;
printf("请输入两个整数>>");
scanf("%d %d", &x, &y);
int ret = p(x, y);
printf("%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:
break;
}
} while (input);
return 0;
}
4.qsort函数使用举例
qsort函数可以排序任意类型的顺序(默认是升序)
我们来解读一下qsort函数中的元素分别表示什么:
通过上面的解读,基本上了解了qsort函数,接下来让我们使用qsort函数来排序整型类型和结构体类型:
4.1 整型类型
#include<stdlib.h>
int cmp_int(const void* p1, const void* p2)
{
return *(int*)p1 - *(int*)p2;
}
void print(int* pa, size_t sz)
{
int i = 0;
while (i < sz)
{
printf("%d ", *(pa + i));
i++;
}
}
int main()
{
int arr[10] = { 9,8,7,6,5,4,3,2,1,0 };
size_t sz = sizeof(arr) / sizeof(arr[0]);
qsort(arr, sz, sizeof(arr[0]), cmp_int);
print(arr, sz);
return 0;
}
4.2 结构体类型
4.2.1 年龄排序
#include<stdlib.h>
struct stu
{
char name[20];
int age;
};
int cmp_stu_age(const void* p1, const void* p2)
{
return ((struct stu*)p1)->age - ((struct stu*)p2)->age;
}
void print(struct stu* ps, size_t sz)
{
int i = 0;
while (i < sz)
{
printf("%s:%d\n", ps->name, ps->age);
ps++;
i++;
}
}
int main()
{
struct stu s[] = {{"liaohanpeng",21},{"niwen",18},{"linshun",20}};
size_t sz = sizeof(s) / sizeof(s[0]);
qsort(s, sz, sizeof(s[0]), cmp_stu_age);
print(s, sz);
return 0;
}
4.2.2 姓名比较
#include<stdlib.h>
#include<string.h>
struct stu
{
char name[20];
int age;
};
int cmp_stu_name(const void* p1, const void* p2)
{
return strcmp(((struct stu*)p1)->name, ((struct stu*)p2)->name);
}
void print(struct stu* ps, size_t sz)
{
int i = 0;
while (i < sz)
{
printf("%s:%d\n", ps->name, ps->age);
ps++;
i++;
}
}
int main()
{
struct stu s[] = { {"liaohanpeng",21},{"niwen",18},{"linshun",20} };
size_t sz = sizeof(s) / sizeof(s[0]);
qsort(s, sz, sizeof(s[0]), cmp_stu_name);
print(s, sz);
return 0;
}
上述代码中的strcmp函数会在接下来的博客中会详细介绍。
5.模拟实现qsort函数
int cmp_int(const void* p1,const void* p2)
{
return *(int*)p1 - *(int*)p2;
}
void print(int arr[], int sz)
{
for (int i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
}
void swap(char* bulf1, char* bulf2, int size)
{
char tem = 0;
for (int i = 0; i < size; i++)
{
tem = *bulf1;
*bulf1 = *bulf2;
*bulf2 = tem;
bulf1++;
bulf2++;
}
}
void bubble_sort2(void * base, size_t sz, size_t size, int (*cmp)(const void* p1,const void* p2))
{
int i = 0, j = 0;
for (i = 0; i < sz - 1; i++)
{
for (j = 0; j < sz - 1 - i; j++)
{
if (cmp((char*)base+j*size, (char*)base + (j+1) * size) > 0)//比较大小
{
//交换
swap((char*)base + j * size, (char*)base + (j + 1) * size,size);
}
}
}
}
void test()
{
int arr[] = { 9,8,7,6,5,4,3,2,1,0 };
int sz = sizeof(arr) / sizeof(arr[0]);
bubble_sort2(arr,sz,sizeof(arr[0]),cmp_int);
print(arr, sz);
}
int main()
{
test();
return 0;
}
(char*)base+j*size是第j个元素的地址, (char*)base + (j+1) * size)是第j+1个元素的地址。