深入理解C语言指针——挑战C指针笔试题 _2(和bug郭一起学C系列)
【摘要】 函数指针首先看一段代码:#include <stdio.h>void test(){ printf("hehe\n");}int main(){ printf("%p\n", test); //函数名 就是函数地址 printf("%p\n", &test); //&函数名 也是函数地址 return 0;}运行结果那么如何将test()函数指针保存起来呢...
函数指针
首先看一段代码:
#include <stdio.h>
void test()
{
printf("hehe\n");
}
int main()
{
printf("%p\n", test); //函数名 就是函数地址
printf("%p\n", &test); //&函数名 也是函数地址
return 0;
}
运行结果
那么如何将
test()
函数指针保存起来呢?
void test()
{
printf("hehe\n");
}
//下面pfun1和pfun2哪个有能力存放test函数的地址?
void (*pfun1)(); //函数指针类型
void *pfun2(); //函数,函数的返回值是void*
函数指针类型
指针都是有类型的
整型指针int*
数组指针int (*)[]
函数指针返回值 (*)(参数....)
#include<stdio.h>
int Add(int x, int y)
{
return x + y;
}
int main()
{
int (*pf)(int, int) = Add;
int sum = (*pf)(3, 5); //对函数指针解引用
printf("sum = %d", sum);
sum = pf(3, 5); //因为 Add和&Add相同,即Add等价于 pf
printf("sum = %d", sum);
return 0;
}
有趣的代码
//代码1
(*(void (*)())0)();
//void (*)()为函数指针
//(void (*)())0 将0强制类型抓换成函数指针的地址
//(*(void (*)())0)() *地址,调用0地址处的这个函数
//函数的返回值空,参数为空
//代码2
void (*signal(int , void(*)(int)))(int);
//void(*)(int) 函数指针,返回值void,参数int
//void (*signal(int , void(*)(int)))(int)
// signal是函数名
//返回值是void(*)(int)
// 参数int 和函数指针 void(*)(int)
//这是一个函数的声明
当我们看到代码2很难看懂这个代码!
可以简化吗?
void (*signal(int , void(*)(int)))(int);
//既然这个函数的返回值类型是 void(*)(int)
//那我们可以写成
// void(*)(int) signal(int , void(*)(int));
//但是这样会语法错误 error
函数指针类型重命名
简化
typedef void(*ptr_t) (int); //正确的类型重命名
ptr_t signal(int, ptr_t); //简化
上面的代码出自《C陷阱和缺陷》
有兴趣的伙伴可以尝试阅读!
函数指针数组
数组是一个存放相同类型数据的存储空间,那我们已经学习了指针数组
比如:
int* arr[10]; //整型指针数组
那要把函数的地址存到一个数组中,那这个数组就叫函数指针数组,那函数指针的数组如何定义呢?
int (*parr1[10]])(); //
int *parr2[10]();
int (*)() parr3[10];
答案是:
parr1
·parr1· 先和[]
结合,说明parr1
是数组,数组的内容是什么呢? 是int (*)()
类型的函数指针。
函数指针数组的用途:
转移表
例子:(计算器)
#include <stdio.h>
int add(int a, int b)
{
return a + b;
}
int sub(int a, int b)
{
return a - b;
}
int mul(int a, int b)
{
return a*b;
}
int div(int a, int b)
{
return a / b;
}
int main()
{
int (*pf[4])(int, int) = { add,sub,mul,div };
int sz = sizeof(pf) / sizeof(pf[1]);
int i = 0;
for (i = 0; i < 4; i++)
{
//实现5和3的加减乘除
printf("%d\n", pf[i](5,3));
}
return 0;
}
//计算器优化
#include <stdio.h>
void menu()
{
printf("*************************\n");
printf(" 1:add 2:sub \n");
printf(" 3:mul 4:div \n");
printf("*************************\n");
printf("请选择:");
}
int add(int a, int b)
{
return a + b;
}
int sub(int a, int b)
{
return a - b;
}
int mul(int a, int b)
{
return a * b;
}
int div(int a, int b)
{
return a / b;
}
void Calc(int (*pf)(int ,int))
{
int x, y;
printf("请输入两个操作数\n");
scanf("%d%d", &x, &y);
printf("%d\n", pf(x, y));
}
int main()
{
int x, y;
int input = 1;
int ret = 0;
int(*p[5])(int x, int y) = { 0, add, sub, mul, div }; //转移表
do
{
menu();//菜单
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;
}
指向函数指针数组的指针
看到这个指针是不是感觉头都大了,没事我们慢慢分析!
int* arr[3]; //整型指针数组
int*(*parr)[3]=&arr; //整型指针数组的地址放入parr
//parr就是指向整型指针数组的指针
//而我们只需要将整型指针换成函数指针就ok了
int(*pf)(int,int);//函数指针
int(*pfarr[3])(int,int);//函数指针数组
int(*(*ppfarr)[3])(int,int)=&pfarr; //&pfarr
//ppfarr是指针,指针指向一个数组,数组的类型为函数指针
//元素个数为3,函数的返回值为int,参数为int
博主就不带大家套娃了,就学到这里了!
回调函数
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
刚刚的计算器优化后就是回调函数,依赖于函数指针。
我们把加减乘除函数的函数指针,传参的形式,传给另一方调用,回调该加减乘除的函数,所以为回调函数!
qsort
函数的使用:
这是MSDN
中对qsort
函数的解释!
我给大家介绍一下!
qsort
作用
Performs a quick sort.
qsort
是快速排序函数库<stdlib.h> and <search.h>
中,我们可以通过qsort
函数实现任意类型数据的排序!
函数声明
void qsort( void *base, size_t num, size_t width, int (__cdecl *compare )(const void *elem1, const void *elem2 ) );
返回值
void
参数
void *base
待排序的内容的首地址
size_t num
待排序元素的个数
size_t width
待排序元素的大小
int (__cdecl *compare )(const void *elem1, const void *elem2 )
自己定义一个排序的方式,例如一个结构体数组,结构体元素,按照结构体里的什么类型的数据的类型排序,所以需要自己定义一个campare
排序方式!
然后将函数指针传参到qsot
中!
//qsort函数使用实例
#include<stdio.h>
struct peo
{
char name[10];
char numb[12];
};
//自己定义一个排序的方式
int compare_char(const void* e1, const void* e2)
{
return strcmp(((struct peo*)e1)->numb, ((struct peo*)e2)->numb);
//按照peo中numb排序
}
int main()
{
//创建一个结构体数组
struct peo peo1[3] = { {"张三","15779770910"},{"李四","17370126640"},{"王五","12669781134"} };
int sz = sizeof(peo1) / sizeof(peo1[1]);
qsort(peo1, sz, sizeof(peo1[1]), compare_char);
for (int i = 0; i < sz; i++)
{
printf("%s\t%s\n",peo1[i].name,peo1[i].numb);
}
return 0;
}
看完
qsort
函数使用演示,是不是学会了。
my_qsort
函数模拟实现
模仿qsort
的功能实现一个通用的冒泡排序!
我们通过qsort
函数的使用,我们尝试一下模拟实现qosrt
函数。
//模拟实现qsort
//模仿qsort的功能实现一个通用的冒泡排序
//void qsort(void* base, size_t num, size_t width,
// int(__cdecl* compare)(const void* elem1, const void* elem2));
#include<stdio.h>
typedef struct student
{
char name[5];
int score;
}Student;
int compare_score(const void*e1,const void* e2)
{
return ((Student*)e1)->score - ((Student*)e2)->score;
}
int compare_name(const void* e1, const void* e2)
{
return strcmp(((Student*)e1)->name, ((Student*)e2)->name);
}
void swap(void* e1, void* e2, size_t width)
{
//将元素一个一个字节交换
int i = 0;
for (i = 0; i < width; i++)
{
char tmp;
tmp = *((char*)e1+i);
*((char*)e1+i) = *((char*)e2+i);
*((char*)e2+i) = tmp;
}
}
void my_qsort(void* base, size_t num, size_t width,
int(*compare)(const void* e1, const void* e2))
{
int i = 0, j = 0,flag = 1;
//趟数
for (i = 0; i < num-1; i++)
{
//每趟比较次数
for (j = 0; j < num - i - 1; j++)
{
//比较
//因为我们无法得知实参的类型,无法得知指针加减的步长
//所以我们强制转换成char*类型指针,通过width元素大小进行访问
if (compare((char*)base + width * j, (char*)base + (j + 1) * width)>0)
{
//交换
swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
flag = 0;
}
}
if (flag == 1)
break;
}
}
int main()
{
Student student[5] = { {"李四",65},{"张三",78},{"王五",56},{"老王",100},{"小李",98} };
my_qsort(student, sizeof(student) / sizeof(student[1]),sizeof(student[0]),compare_score);
int i = 0;
for (i = 0; i < 5; i++)
{
printf("%s %d\n", student[i].name, student[i].score);
}
return 0;
}
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)