C语言整理-11

函数指针

在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

//返回最大值

 

//函数的地址:函数名

//函数的类型:返回值类型 +参数个数和参数类型。

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

//练习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

//练习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, p1p1 = maxValue此时,进到函数内,执行pv(num1,num2);pv为函数指针,此时,我们的p1将自己所指向的函数地址,给pv。那么此时的pvnum1num2两个参数给到它所指向的那个函数int sum(int num1,int num2)去执行,得到的结果再返回给用户。这样就可以起到保护核心代码的作用,为什么这么说?因为我可以不用改那个函数里边的代码,只是在这个函数的参数预留你想要的接口(一般是函数指针)。此时,只需要给这个参数一个函数地址,即可找到一个函数实现你想要的结果,不需要再改变原来函数的代码。

一些例子:

写⼀函数查找成绩90分以上的学员,使⽤回调函数在姓名后加”⾼富帅”。

#import

//练习

 

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

//从小到大排序

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

//学生结构体

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

//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语言整理)