【20180829】【C/C++基础知识】函数指针,函数与结构

函数指针

请问:变量有地址,函数有没有地址呢?

答:函数名也是地址,调用函数时,就是通过函数地址跳转到函数定义的地方执行函数体!既然是地址,那就可以用指针处理,处理指针的函数叫函数指针。

  • 函数指针:指向函数的指针,简称函数指针。指向了函数的第一条指令的地址,即存储的是函数的入口地址。
  • 我们可以这样理解:将函数理解为一个变量,只是这个变量是一段可执行的指令。
  • 使用函数指针可以编写更通用、更灵活的程序。

程序也存储在内存中,与数据一样。

函数的入口地址:函数的第一条指令的地址,称为函数的入口地址。

函数的入口地址:编译器将不带()的函数名解释为函数的入口地址。

调用函数时,实际就是转到函数的入口地址,去执行入口地址的第一条指令。

函数指针和函数原型很类似,只是函数名称前面有一个星号*,并且和函数返回值是指针不同,这里的函数名和指针*要用小括号括起来,表示这个整体是一个指针,这个指针要指向这种函数原型的函数。

函数指针的定义:

数据类型 (*指针名)(参数列表);

例如:

解释:定义一个函数指针add,它指向这样一个函数:该函数的返回值是整型类型,并且有两个整型参数。

定义函数指针时的常见错误:

(1) int *add(int a, int b); // 定义了一个函数add,函数返回值为指向整型数据的指针,并具有两个整型的参数。(其实就只是定义了一个普通函数,在前面加星号*而没有括号只是表明返回值是整型数据的指针,而并不是函数指针!)

(2) int (*add);  // 定义了一个指向整型的指针(它就只是一个普通的整型指针,并不指向函数!)

例1:看看如何使用函数指针,以及函数指针有什么作用。

/* 看看如何使用函数指针,以及函数指针有什么作用 */
#include <stdio.h>
#include<stdlib.h>

int add(int a, int b)
{
    return(a+b);
}
int sub(int a, int b)
{
    return(a-b);
}
int add(int a,int b);  // 函数声明有分号

int sub(int a,int b);

int main()   
{
    int (*pf)(int, int);  // 定义一个函数指针*pf,返回值是整型,两个参数也是整型
    int a,b,c;
    a=1,b=2;
    pf=add;     // add函数首地址赋给函数指针pf
    c=(*pf)(a,b);  // 通过pf去调用它所指的函数(即add函数),传递的参数为a和b,函数返回值放在c中
    printf("%d+%d=%d\n",a,b,c);
    pf=sub;    // sub函数首地址赋给函数指针pf
    c=pf(a,b); // 当作函数指针使用,不加星号*
    printf("%d-%d=%d\n",a,b,c);
   
    system("pause");
    return 0;
}

两种调用形式可以互换:

第一种:把它作为指针,调用指针所指对象(指针所指对象是函数)

第二种:把这个指针当作函数指针使用(就可以不加括号和星号*)

函数指针的作用:

可以以一种形态去处理不同的函数,达到了多态的效果。

函数与结构

请问:结构指针(结构和指针结合起来得到的)和结构分别作为函数参数与返回值相比,有哪些优势?

答:当结构变量和结构指针作为参数和返回值时,效率上有很大区别!

如果把结构变量成员、结构变量、结构指针这三类分别作为函数参数与函数返回值,那么就有6种不同的情况:

结构指针传递的都是地址,是通过指针直接对实参的结构空间进行操作,避免了结构空间的分配与成员数据拷贝,效率更高。因此最好用指针进行操作,即用指针变量作为函数参数进行传递,这样由实参向形参传递的只是地址,从而减少了时间和空间的开销,指针作为函数返回值也是一样的道理!

例2:计算一组学生的平均成绩和不及格人数。

分析:假设学生有学号、姓名、性别和成绩。根据要求,我们要有每个学生的成绩,然后根据输入的成绩计算平均成绩并输出。我们给函数取名ave,参数是结构数组首地址和人数,因为函数内部输出了平均成绩,不再需要主调函数输出,所以不需要函数返回值,即函数返回值是void。(这种分析有问题!)

/* 计算一组学生的平均成绩和不及格人数 */
#include <stdio.h>
#include<stdlib.h>
struct stu{        // 定义stu结构体:学号、姓名、性别、成绩
        int num;
        char *name;
        char sex;
        float score;
    }
    boy[5]={        // 5个学生信息
        {101,"Li ping",'M',45},
        {102,"Zhang ping",'M',63.5},
        {103,"He fang",'F',92.5},
        {104,"Cheng ling",'F',87},
        {105,"Wang ming",'M',58}
    };

void ave(struct stu *ps,int n)   // 求平均成绩函数
{
    int c=0,i;    // c是不及格人数
    float ave,s=0;
    for(i=0;i<n;i++,ps++)
    {
        s+=ps->score;
        if(ps->score<60)
            c+=1;
    }
    printf("s=%f\n",s);
    ave=s/n;
    printf("average=%f\nNopass student count=%d\n",ave,c);
}
void main()   
{
    void ave(struct stu *ps);
    ave(boy,5);

    system("pause");
    //return 0;
}

这样的分析是有问题的,因为函数返回的结果不是在函数内部把计算结果打在屏幕上就好了,而是应该通过通过函数参数或函数返回值获得计算结果,而不是在函数内部输出结果。调用者想怎么利用函数的结果,那是由调用者的需要决定的,而不是由函数自己决定以什么方式来输出数据。

返回结果不是在函数内部将计算结果打印在屏幕上!

因此我们修改函数的原型,把平均成绩和不及格人数通过返回值给主调函数,但函数的返回值只有一个,因此我们将其中一个返回值作为函数的参数传递给主调函数(传地址调用),因此要返回地址(要加*和&!)。

修改函数原型为:int ave(struct stu *ps, int n, float *average);

第一个参数:数组的首地址

第二个参数:数组元素个数

第三个参数:返回的平均成绩

/* 计算一组学生的平均成绩和不及格人数 */
#include <stdio.h>
#include<stdlib.h>
struct stu{        // 定义stu结构体:学号、姓名、性别、成绩
        int num;
        char *name;
        char sex;
        float score;
    }
 boy[5]={        // 5个学生信息
        {101,"Li ping",'M',45},
        {102,"Zhang ping",'M',63.5},
        {103,"He fang",'F',92.5},
        {104,"Cheng ling",'F',87},
        {105,"Wang ming",'M',58}
    };
int ace(struct stu *ps,int n,float *average);
int ave(struct stu *ps,int n,float *average)   // 求平均成绩函数,需要返回不及格人数,因此函数类型为int型
{
    int c=0,i;
    float *ave,s=0;
    for(i=0;i<n;i++,ps++)
    {
        s+=ps->score;
        if(ps->score<60)
            c+=1;
    }
    *average=s/n;
    return c;   // 函数返回值返回不及格人数
}

void main()   
{
    float average;
    int m;
    m=ave(boy,5,&average);  // 不及格人数通过函数返回值返回,那么平均分就要用参数传递的方式输出
                            // 因为要实现双向传递,因此要用传地址调用!
    printf("avergae is %.2f.No pass count=%d\n",average,m);
   
    system("pause");
    //return 0;
}

 

心得:

根据需要,如果它有独立的功能,就可以用函数来实现,根据已有的信息和想要得到的结果,我们给出函数的原型,在函数体内部,通过算法将输入转换为输出。函数的输出:通过返回值、或者通过输出参数得到(传地址调用实现双向传递),不要在函数内部使用printf函数输出结果!不能简单地认为函数体内部printf函数就是函数的输出!!!

疑问:两个结果相同,为什么第一种不可以???

补充

(1)指针函数和函数指针(参考:点击此处查看:博主写得十分棒了,手动点赞!

指针函数:本质是函数,返回值为指针(即返回一个地址值),int *ave(int a, int b);

函数指针:本质是指针,该指针指向某个函数(用于调用函数,调用方式有两种(见下面)),int (*ave)(int a, int b);

函数指针用于调用函数,调用方式下面有两种:

(a) p=*ave();

(b) p=ave();

(2)函数的输出方式

函数的输出方式只有两种:

(a) 通过函数返回值返回

(b) 参数传递(双向传递):传地址调用

注意:不能在函数体内部使用printf函数输出!源程序输出要在主函数内输出!!!

(3)结构体‘.’和'->'的区别(参考:点击此处查看

(a) 如果定义的是结构体变量,那么用'.',例如:struct stu p;  p.num;

(b) 如果定义的是结构体指针,那么用'->',例如:struct stu *p;  p->num;

(*p)->num是错误的,因为p是指针,*p就是值类型,那么就应该用(*p).num。即(*p).num=p->num。

猜你喜欢

转载自blog.csdn.net/weixin_40583722/article/details/82191100