文章目录
- 前言
- 一、函数指针的使用
- 1. 加减乘除计算器普通实现
- 2. 加减乘除计算机函数指针实现
- 二、函数指针数组
- 1. 函数指针数组的书写
- 2. 两个有趣的代码
- 3. 函数指针数组的使用
- 三、指向函数指针数组的指针
- 四、指针进阶_冒泡排序
- 1.整型冒泡排序
- 2. C语言qsort函数
- 3. 仿写C语言qsort库函数
- 总结
前言
C语言函数指针的使用、函数指针数组及使用、指向函数指针数组的指针及使用,C语言库函数qsort、指针进阶版的冒泡排序等介绍
一、函数指针的使用
1. 加减乘除计算器普通实现
#include <stdio.h>void menu()
{printf("***************************\n");printf("***** 1. Add 2. Sub******\n");printf("***** 3. Mul 4. Div******\n");printf("***** 0. Quit ******\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;int y = 0;int ret = 0;do{menu();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 :printf("输入错误\n");break;}} while (input);return 0;
}
2. 加减乘除计算机函数指针实现
- 在上述基本实现中,每种情况(case)的代码大量冗余,可以使用函数指针来优化。
- 每种情况都调用 calc 函数,传入要进行运算的函数地址。
- 使用函数指针可以减少大量代码的冗余。
#include <stdio.h>void menu()
{printf("***************************\n");printf("***** 1. Add 2. Sub******\n");printf("***** 3. Mul 4. Div******\n");printf("***** 0. Quit ******\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;int y = 0;int ret = 0;printf("请输入两个操作数:");scanf("%d %d", &x, &y);ret = pf(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:printf("输入错误\n");break;}} while (input);return 0;
}
二、函数指针数组
- 存放函数指针的数组就是函数指针数组。
1. 函数指针数组的书写
// 函数指针的书写
int Add(int x, int y);
int (*pf)(int, int) = &Add;
// 以上为函数指针的书写,函数指针数组的书写与函数指针可以通过函数指针的修改来得到
// 函数指针数组,顾名思义,一个数组的每个元素的类型都是函数指针。
int (*pfarr[5])(int, int) = { Add, Sub, Mul, Div};
// Add Sub Mul Div 均为函数名,即函数的地址
// pfarr[5] 是一个数组名为pfarr,数组元素个数为5 的数组
// 这个数组的元素类型是 int (*)(int, int),即函数指针类型。
2. 两个有趣的代码
( *(void(*)())0)();
- 上述代码,void(*)() 是一个函数指针类型,带括号跟0,是将0强制转化为函数指针类型。
- 最后再调用转换后的 0 地址对应的函数,没有参数。
void(*signal(int, void(*)(int)))(int);
- 上述代码,本质上是一个函数的声明,函数名是 signal。
- signal函数的参数有两个,一个是 int 即 整型类型。
- 一个是 void(*)(int) 即 函数指针类型的,并且这个函数指针指向的函数参数有一个 int 即整型类型,并且返回值的类型是 void。
- signal函数的返回值是 void(*)(int)即函数指针类型,也就是说它返回了一个函数的地址。
上述 2 中的代码可以简化为更好理解的形式借助 typedef
typedef void (*pf_t)(int); // 这个语句将 void (*)(int) 类型重命名为 pf_t
// 所以 2 中的代码可以写成
pf_t signal(int ,pf_t);
// 这样就非常清晰了,signal 函数的返回值是 pf_t, 参数类型 为 int 和 pf_t
3. 函数指针数组的使用
- 以 一、函数指针的使用中的计算器为例。
- 使用函数指针数组,省略了switch分支语句的使用,更加节省了代码量。
- 最为关键的是,如果将来计算机需要增加功能,可以直接再函数指针数组中加入功能函数的地址即可。便于新增或减少计算器功能。
#include <stdio.h>void menu()
{printf("***************************\n");printf("***** 1. Add 2. Sub******\n");printf("***** 3. Mul 4. Div******\n");printf("***** 0. Quit ******\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;int y = 0;do{menu();printf("请选择:>");scanf("%d", &input);----------------------------------------------------------------int (*pfarr[5])(int, int) = {0, Add, Sub, Mul, Div};int sz = sizeof(pfarr) / sizeof(pfarr[0]);if (0 == input){printf("退出游戏\n");break;}else if (input >= 1 && input < sz){printf("请输入两个操作数:>");scanf("%d %d", &x, &y);int ret = pfarr[input](x, y);printf("%d\n", ret);}else{printf("输入错误\n");}----------------------------------------------------------------} while (input);return 0;
}
三、指向函数指针数组的指针
简单介绍
- 通过函数指针数组的书写 -----> 指向函数指针数组的指针
int (*pfarr[5])(int, int) = {Add, Sub, Mul, Div};// 指向函数指针数组的指针,则需要取地址 函数指针数组
&pfarr;
// 用 *ppfarr来接收 指向函数指针数组的指针
// (*ppfarr)[5] 说明 指向函数指针数组的指针 所指向的数组有 5 个元素
// int (* (*ppfarr)[5])(int, int) 说明 指向函数指针数组的指针 所指向的数组元素的类型为 int(*)(int, int)
// 即函数指针
int (* (*ppfarr)[5])(int, int) = &pfarr;
四、指针进阶_冒泡排序
1.整型冒泡排序
#include <stdio.h>void bubble_sort(int arr[], int sz)
{int i = 0;int j = 0;int tmp = 0;for (i = 0; i < sz - 1; i++){for (j = 0; j < sz - 1 - i; j++){if (arr[j] > arr[j + 1]){tmp = arr[j];arr[j] = arr[j + 1];arr[j + 1] = tmp;}}}
}int main()
{int arr[10] = { 9,8,7,6,5,4,3,2,1,0 };int sz = sizeof(arr) / sizeof(arr[0]);bubble_sort(arr, sz);int i = 0;for (i = 0; i < sz; i++){printf("%d ", arr[i]);}return 0;
}
2. C语言qsort函数
void qsort( void *base, size_t num, size_t width, int (__cdecl *compare )(const void *elem1, const void *elem2 ) );
- C语言中qsort函数的定义如上所示,
- base 是 数组的起始地址,为空指针,可以接收任何类型的地址。
- num 是 数组元素的个数。
- width 是 一个数组元素所占字节大小。
- compare指向一个比较函数,比较函数有两个参数,分别为要比较的两个数。
注意:空指针是指没有具体类型指针,可以接受任何类型的指针,但是不能直接被解引用的
3. 仿写C语言qsort库函数
- C语言的qsort函数可以比较任意类型的数据大小,并按照快速排序法进行排序。
- 我们这里采用 冒泡排序仿写 qsort。
- 函数名 bubble_sort
- 函数参数 :
-
- 空指针 base 为了接收任何类型的数据
-
- sz 数组元素的大小
-
- width 一个数组元素所占字节大小
-
- *cmp 指向一个比较函数, 如:整形比较 cmp_int ,这个函数如果传入参数第一个大于第二个则返回大于零的数,如果相等,则返回0,如果小于,返回-1,可以直接用第一个数减去第二个数得到。
-
- swap 交换函数,传入两个值的地址,一个字节一个字节的进行交换。
- 整型冒泡排序
#include <stdio.h>
--------------------------------------------------------
// 比较整型的具体函数,它的调用者在bubble_sort中
int cmp_int(void* e1, void* e2)
{/*if (*(int*)e1 > *(int*)e2){return 1;}else if (*(int*)e1 == *(int*)e2){return 0;}else{return -1;}*/return (*(int*)e1 - *(int*)e2);
}
--------------------------------------------------------
// 交换函数 交换两个数的值,一个字节一个字节交换 它的调用者在bubble_sort中
void Swap(char* buff1, char* buff2, int width)
{int i = 0;for (i = 0; i < width; i++){char tmp = *buff1;*buff1 = *buff2;*buff2 = tmp;buff1++;buff2++;}
}
--------------------------------------------------------
// 进行冒泡排序的逻辑
void bubble_sort(void* base, int sz, int width, int (*cmp)(void* e1, void* e2))
{int i = 0;int flag = 1;// 如果为有序数组,进行一趟排序,如果没有交换,则直接跳出for (i = 0; i < sz-1; i++){int j = 0;for (j = 0; j < sz-1-i; j++){if (cmp((char*)base + (j)* width , (char*)base + (j + 1)* width) > 0){ // 调用比较函数Swap((char*)base + (j)*width, (char*)base + (j + 1) * width, width);// 调用交换函数flag = 0;}}if (1 == flag){break;}}
}
--------------------------------------------------------
// 调用整型冒泡排序
void test1()
{int arr[10] = { 9,8,7,6,5,4,3,2,1,0 };int sz = sizeof(arr) / sizeof(arr[0]);bubble_sort(arr, sz, sizeof(arr[0]), cmp_int);int i = 0;for (i = 0; i < sz; i++){printf("%d ", arr[i]);}
}
--------------------------------------------------------
//主函数
int main()
{test1(); // 整型排序//test2(); // 结构体中name字符的排序return 0;
}
执行结果如下:
- 结构体内字符串排序
#include <stdio.h>
-----------------------------------------------------------------
// 声明结构体类型
struct Stu
{char name[20];int age;
};
-----------------------------------------------------------------
// 结构体内字符串比较函数
int cmp_struct_by_name (void* e1, void* e2)
{return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name);// strcmp 函数返回的值 正好是 1 0 -1
}
-----------------------------------------------------------------
// 交换函数
void Swap(char* buff1, char* buff2, int width)
{int i = 0;for (i = 0; i < width; i++){char tmp = *buff1;*buff1 = *buff2;*buff2 = tmp;buff1++;buff2++;}
}
-----------------------------------------------------------------
void bubble_sort(void* base, int sz, int width, int (*cmp)(void* e1, void* e2))
{int i = 0;int flag = 1;// 如果为有序数组,进行一趟排序,如果没有交换,则直接跳出for (i = 0; i < sz-1; i++){int j = 0;for (j = 0; j < sz-1-i; j++){if (cmp((char*)base + (j)* width , (char*)base + (j + 1)* width) > 0){Swap((char*)base + (j)*width, (char*)base + (j + 1) * width, width);flag = 0;}}if (1 == flag){break;}}
}
-----------------------------------------------------------------
// 调用比较结构体内部的字符的排序
void test2()
{struct Stu S[3] = { {"zhangsan", 15}, {"lisi", 50},{"wangwu", 35} };int sz = sizeof(S) / sizeof(S[0]);bubble_sort(S, sz, sizeof(S[0]), cmp_struct_by_name);int i = 0;for (i = 0; i < sz; i++){printf("%s ", S[i].name);}
}
-----------------------------------------------------------------
int main()
{//test1(); // 整型排序test2(); // 结构体中name字符的排序return 0;
}
执行结果如下:
- 结构体内整型排序
#include <stdio.h>
--------------------------------------------------------------------
// 结构体类型声明
struct Stu
{char name[20];int age;
};
int cmp_struct_by_age(void* e1, void* e2)
{return (((struct Stu*)e1)->age - ((struct Stu*)e2)->age);
}
--------------------------------------------------------------------
// 交换函数定义
void Swap(char* buff1, char* buff2, int width)
{int i = 0;for (i = 0; i < width; i++){char tmp = *buff1;*buff1 = *buff2;*buff2 = tmp;buff1++;buff2++;}
}
--------------------------------------------------------------------
// 冒泡函数定义
void bubble_sort(void* base, int sz, int width, int (*cmp)(void* e1, void* e2))
{int i = 0;int flag = 1;// 如果为有序数组,进行一趟排序,如果没有交换,则直接跳出for (i = 0; i < sz-1; i++){int j = 0;for (j = 0; j < sz-1-i; j++){if (cmp((char*)base + (j)* width , (char*)base + (j + 1)* width) > 0){Swap((char*)base + (j)*width, (char*)base + (j + 1) * width, width);flag = 0;}}if (1 == flag){break;}}
}
--------------------------------------------------------------------
//调用排序结构体类型中的整型
void test3()
{struct Stu S[3] = { {"zhangsan", 15}, {"lisi", 50},{"wangwu", 35} };int sz = sizeof(S) / sizeof(S[0]);bubble_sort(S, sz, sizeof(S[0]), cmp_struct_by_age);int i = 0;for (i = 0; i < sz; i++){printf("%d ", S[i].age);}
}
--------------------------------------------------------------------
int main()
{//test1(); // 整型排序//test2(); // 结构体中name字符的排序test3(); // 结构体中的age排序return 0;
}
执行结果如下:
总结
C语言函数指针的使用、函数指针数组及使用、指向函数指针数组的指针及使用,C语言库函数qsort、指针进阶版的冒泡排序等介绍