文章目录
- 前言
- 一、函数指针的使用
- 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、指针进阶版的冒泡排序等介绍