C语言整理-11
来源:互联网 发布:java同名包类加载顺序 编辑:程序博客网 时间:2024/06/08 15:07
函数指针
在C语言里,与数组的数组名是数组的首地址一样,一个函数的函数名,也是该函数的首地址。该怎么使用呢?
例子:定义一个函数如下:
int maxValue(inta,int b) {
return a>b?a:b;
}
那么这个函数的地址就是maxValue。
此时引出“函数指针”的定义方式:
int( *p)(int , int)=NULL;
这个该怎么解释?
“定义一个p变量,这个变量是一个指针,这个指针指向一个函数,函数的参数有两个,都是int类型的,这个函数的返回值是int类型的。”
当任意多个函数的参数个数、参数类型、返回值类型都一样时,这个函数的类型就是一样的。因此在定义函数指针的时候,可以定义一个指向NULL的函数指针,然后分别将各个函数名赋值给这个指针。即:
int( *p)(int,int)=NULL;
p = maxValue;
定义好后,就可以直接使用这个p了,与普通调用函数一样,只需要将其写下来,把参数给上,接收返回值就行了。
int num = p(3,5);
一些练习,定义几个函数,分别定义这些函数的函数指针。
#import <Foundation/Foundation.h>
//返回最大值
//函数的地址:函数名
//函数的类型:返回值类型 +参数个数和参数类型。
int maxValue(int num1,int num2 ){
return num1 > num2 ? num1 : num2;
}
//a函数和maxValue的函数类型相同,可以使用相同的函数指针。
int a(int n1,int n2){
return 1;
}
//把一下函数的函数指针写出来
void b(float n1,float n2){
}
void c(){
}
float d(int n1){
return 1.2;
}
int main(int argc,constchar * argv[])
{
//2015-04-09 09:47:33北京
// //定义一个函数指针的方式
// int(*p1)(int,int) = maxValue;
// //a函数和maxValue的函数类型相同,可以使用相同的函数指针。
//// p1 = a;
// //b函数的指针
// void (*p2)(float , float) = b;
// //c函数的指针
// void (*p3)() = c;
// //d函数的指针
// float (*p4) (int) = d;
//
// printf("%d\n",maxValue(10, 20));//20
//
// int n1 = p1(10,20);//指针名调用
// printf("%d\n",n1);//20
return 0;
}
一个练习:
void printHello();
定义⼀个可以指向上述函数的函数指针,并通过函数指针实现调⽤该函数。
#import <Foundation/Foundation.h>
//练习1
void printHello(){
printf("hello world!\n");
}
int main(int argc,constchar * argv[])
{
//l练习
// //函数指针p1
// void(*p1)() = printHello;
// //指针调用
// p1();//hello world!
return 0;
}
函数回调:
一个练习:
定义两个函数,⼀个求最⼤值,⼀个求和,输⼊max或sum分别求3,5的最⼤值或和(提⽰,定义⼀个函数指针,根据输⼊内容指向不同函数,最后⼀次调⽤完成)。
#import <Foundation/Foundation.h>
//练习2
int sum(int num1,int num2){
return num1 + num2;
}
//获得一个值
//函数指针做参数,可以屏蔽核心代码
int getValue(int num1,int num2,int (*pv)(int,int)){
//核心代码
//通过指针再去调用函数,称为回调。
return pv(num1,num2);
}
int main(int argc,constchar * argv[])
{
// char str[20] = {0};
// printf("请输入maxValue或者sum完成不同功能:");
// scanf("%s",str);
//
// int(*pf)(int,int) = NULL;
//
// if (strcmp("maxValue", str) == 0) {
// pf = maxValue;
// }else if(strcmp("sum", str) == 0){
// pf = sum;
// }else{
// printf("输入有误!");
// }
// //调用函数
// if (pf != NULL) {
// int n = pf(3,5);
// printf("%d\n",n );
// }else{
// return 0;
// }
//函数回调:函数只在乎你作为函数参数
// int (*p1) (int ,int ) = maxValue;
// int (*p2) (int ,int ) = sum;
//
// getValue(3, 5, p1);//5
// getValue(5, 6, p2);//11
return 0;
}
在一个函数中,如果将一个函数指针作为它的一个参数,那么这个函数指针可以起到一个保护核心代码的作用。
如上个例子,在getValue()中,定义了int num1,num2,int(*pv)(int int)三个参数,其中第三个参数为函数指针。那么此时调用getValue()这个函数的时候,接收到三个参数3, 5, p1。p1 = maxValue。此时,进到函数内,执行pv(num1,num2);pv为函数指针,此时,我们的p1将自己所指向的函数地址,给pv。那么此时的pv将num1和num2两个参数给到它所指向的那个函数int sum(int num1,int num2)去执行,得到的结果再返回给用户。这样就可以起到保护核心代码的作用,为什么这么说?因为我可以不用改那个函数里边的代码,只是在这个函数的参数预留你想要的接口(一般是函数指针)。此时,只需要给这个参数一个函数地址,即可找到一个函数实现你想要的结果,不需要再改变原来函数的代码。
一些例子:
写⼀函数查找成绩90分以上的学员,使⽤回调函数在姓名后加”⾼富帅”。
#import <Foundation/Foundation.h>
//练习
typedefstruct Student{
char name[20];
float score;
}Student;
void search(Student stuArr[],int count,void (*ps)(Student *)){
for (int i = 0; i < count ; i++) {
if ((stuArr+i)->score >=90) {
//添加"高富帅"
ps(&stuArr[i]);
}
}
}
void setName(Student *stu){
strcat(stu->name,"高富帅");
}
int main(int argc,constchar * argv[]){
//练习
// Student s1 = {"王大锤",90};
// Student s2 = {"王大",89};
// Student s3 = {"王大",99};
// Student s4 = {"王大",91};
//
// Student stu_arr[4] = {s1,s2,s3,s4};
// void (*p1)(Student *) = setName;
//
// search(stu_arr, 4,p1);
//
// for (int i = 0; i < 4; i ++) {
// printf("%s\n",stu_arr[i].name);
// }
return 0;
}
上面这些,将函数指针作为函数参数的做法,叫做函数回调。
说白了,就是在函数里调其他函数,只是不是明调罢了,只是将地址给它,让它根据地址去指定的地方执行。
动态排序:
动态排序的意思是根据不明确的需求(即会根据不同的条件,进行不同的排序),来对数据进行排序。这个是函数回调(函数指针做函数参数)的一个经典例子。这类问题的关键是,根据不同的条件,这些“不同的条件”就是关键。
#import <Foundation/Foundation.h>
//从小到大排序
void sort (int arr[],int count,BOOL(*pf)(int,int)){
for (int i = 0; i < count -1 ; i ++) {
for (int j = 0; j < count - (i + 1); j ++) {
if( pf(arr[j] , arr[j+1]) ){//判断是关键
arr[j] = arr[j] ^ arr[j + 1];
arr[j+1] = arr[j] ^ arr[j+1];
arr[j] = arr[j] ^ arr[j + 1];
}
}
}
}
BOOL cmpFunc(int num1,int num2){
return num1 > num2;
}
int main(int argc,constchar * argv[])
{
//动态排序
// int arr[5] = {4,3,1,2,5};
//
// BOOL (*p1)(int,int) = cmpFunc;
//
//
// sort(arr,5,p1);
//
// printf("小到大排序:\n");
// for (int i = 0 ; i < 5 ; i ++) {
// printf("%d\t",arr[i]);
// }
return 0;
}
冒泡排序,在if这个条件中,放入pf(arr[j] , arr[j+1])这个函数指针,可根据不同的返回值,进行不同的排序。
一个例子:
#import <Foundation/Foundation.h>
//学生结构体
typedefstruct Student{
char name[20];
float score ;
int age;
}Student;
void sortStudent(Student stu[],int count,BOOL (*p)(Student *,Student *)){
for (int i = 0; i < count -1 ; i ++) {
for (int j = 0; j < count - i -1; j++) {
if(p(&stu[j],&stu[j+1])){
Student temp = {0};
temp = stu[j];
stu[j] = stu[j+1];
stu[j+1] = temp;
}
}
}
}
//按成绩排
BOOL byScore(Student *s1 ,Student *s2){
return s1->score > s2->score;
}
//按年龄排
BOOL byAge(Student *s1 ,Student *s2){
return s1->age > s2->age;
}
//按姓名排序
BOOL byName(Student *s1 ,Student *s2){
if (strcmp(s1->name , s2->name)>0) {
returnYES;
}else{
returnNO;
}
}
int main(int argc,constchar * argv[])
{
// 2015-04-09 15:13:52北京
Student stu[5] = {{"a",99,16},{"b",70,19},{"c",88,20},{"d",60,15},{"e",80,0}};
// BOOL (*sp)(Student *,Student *) = byScore;
BOOL (*ap)(Student *,Student *) =byAge;
BOOL (*np)(Student *,Student *) =byName;
// sortStudent(stu, 5,sp);
sortStudent(stu, 5, ap);
sortStudent(stu, 5, np);
// sortStudentByName(stu, 5, np);
for (int i = 0; i < 5; i ++) {
printf("%s:%.2f:%d\n",stu[i].name,stu[i].score,stu[i].age);
}
return 0;
}
函数指针作为返回值:
这里函数指针作为返回值,用到了typedef的第二种用法,专门给一个函数指针类型取一个名字。这样就可以直接用这个名字来作为另一个函数的返回值。
其实,* 号可以写在typedefint *pFun(int,int);也可以写在定义函数的时候pFun *fun2(char str[])。看情况,没有具体要求。
//typedef的第二种用法,专门给函数指针类型取一个心名字
typedefint pFun(int,int);
pFun *fun2(char str[]){
returnNULL;
}
一个例子:
#import <Foundation/Foundation.h>
//typedef的第二种用法,专门给函数指针类型取一个心名字
typedefint pFun(int,int);
pFun *fun2(char str[]){
returnNULL;
}
// + - * /
//定义了加减乘除四个函数,每个函数首地址都为函数名。
//四个函数类型相同。
int sum(int num1 ,int num2){
return num1 + num2;
}
int sub(int num1 ,int num2){
return num1 - num2;
}
int mul(int num1 ,int num2){
return num1 * num2;
}
int di(int num1,int num2){
if (num2!=0) {
return num1 / num2;
}else{
return 0;
}
}
//给函数类型起了一个新名字。Cal。
typedefint Cal(int ,int );
//定义一个结构体,用来一一对应名字和函数指针匹配(名字与函数建立对应关系)
typedefstruct FunNamePair{
char name[20];
Cal *pFun;
}FunNamePair;
Cal *searchFun(char str[]){
FunNamePair f[4] = {{"sum",sum},{"sub",sub },{"mul",mul },{"di",di }};
for (int i = 0; i < 4; i ++) {
if (strcmp(str, f[i].name) == 0) {
return f[i].pFun;
}
}
returnNULL;
}
int main(int argc,constchar * argv[])
{
// 2015-04-09 16:00:49北京
Cal *p = searchFun("mul");
if(p !=NULL){
printf("%d\n",p(3,5));
}else{
printf("输入有误。");
}
return 0;
}
这样,C语言告一段落。
- C语言整理-11
- 2017/11/29C语言笔记整理
- C语言基础整理
- C语言基础整理
- C语言知识整理
- 理论知识整理(C语言)
- C 语言整理
- C 语言知识整理
- C 语言整理(一)
- c语言 温习整理
- C语言整理-1
- C语言整理-2
- C语言整理-3
- C语言整理-4
- C语言整理-5
- C语言整理-6
- C语言整理-7
- C语言整理-8
- MFC DestroyWindow、OnDestroy、OnClose 程序关闭相关
- Android WebView中JAVA与JS之间的传递(一)
- 中断与异常详解(二)
- java中获取根异常并进行分发处理
- 【LeetCode从零单刷】Kth Smallest Element in a BST
- C语言整理-11
- eclipse背景颜色修改插件color theme
- 一步一步学习Python(使用bat快捷启动)
- net大型分布式电子商务架构说明
- MessageQueue, Looper, Thread, Handler, Message, Messenger
- Spring简介
- LeetCode 70: Climbing Stairs
- JavaScript传参的问题
- 中断与异常详解(三)