从C出发 29 --- 指针与函数

只有知道具体位置,才有可能跳到那个位置去执行,如果不知道在哪里,怎么跳?

函数是什么? 函数就是一片连续的内存

数组是什么? 数组就是一片连续的内存

很显然,这一片连续的内存当中,保存的就是我们所编写的一行一行的代码

因此,我们可以推论出,真正调用函数时,跳转到这一片内存的起始位置处执行

这个位置的具体地址如何获得?

        对比数组,函数名

函数和数组的本质都是连续的内存,只不过内存中的东西不一样而已,对于数组来说这一片连续的内存中存放的是一个一个的数据,对于函数来说,这一片连续的内存中存放的是一行一行的代码,本质是一样的

数组有自己独特的类型,函数也有自己独特的类型

函数类型由返回类型和参数类型所共同组成

 

 

 func 函数     的类型是     Type(Type1, Type2)  ,于是可进一步知道函数名所对应的类型是什么了,函数名是函数的入口地址,因此所对应的类型必然是一个指针类型,这个类型为 Type(*)(Type1,Type2)由函数类型和 * 号共同组成,因此现在可以定义指针来指向函数了,

怎么做 ?

这里的pFunc 是指针,这个指针指向 Type(Type1, Type2) 这样一个东西,这样一个东西是函数,所以知道 pFunc 指向函数,并且只能指向  Type(Type1, Type2) 这一款函数,具体指向谁?你不初始化,你不赋值,怎么知道这个指针指向谁,

所以有了这样的写法           Type (*pFunc)(Type1,Type2) = func  ,

这样写就是想把 func 这一个名字所对应的函数入口地址用于初始化这个指针  *pFunc  

为什么不这么写?

&func

对比前面的数组指针,肯定要加取地址符号啊,这个地方为什么直接是函数名,没有加取地址符号呐?

对一个函数来说,它的名字就是入口地址,加不加取地址符,都是一样的,数值一样,意义一样,加不加一样

 add  和  mul   函数是同款,同款意味着类型相同,都是这个类型函数  int (int ,int)

pFunc 是一个指针,这个指针指向函数,指向什么类型的函数,指向  int (int ,int ) 这个类型的函数,返回值为  int ,有2 个 int 参数的函数

NULL    用  0 值来将这个指针进行初始化,因为 pFunc 再怎么复杂也是指针,既然是指针,就离不开本质,保存的就是内存地址

pFunc = add;                使得pFunc  指向  add, 指向了add ,就可以用 pFunc 来调用add 函数了,

pFunc 和 *pFunc  写法不同,意义相同  

pFunc(1, 2);   直接通过函数指针进行调用,为什么,因为函数指针里面保存的就是函数的入口地址

*pFunc(1, 2);  pFunc 是指针,那么这个 * 访问操作符作用于指针之后, 就意味着把这个指针所指向的东西给拿到,那么这个指针所指向的东西是什么呐?是函数,所以说就把函数拿出来进行调用

这2 种写法的效果是一模一样的,具体怎么写就看心情了

pFunc = &mul;     做了赋值操作, 使用mul 函数的地址赋值给pFunc指针,对一个函数来说,函数名就是入口地址,写不写& 都一样

真正工程上不会这么写,直接调用函数就可以了

在工程上使用函数指针会这么来玩,      写相同的代码,实现不同的功能

如果要调用 calculate  函数,那么有一个参数是函数,以前我们所接触的函数都是使用变量作为实参来调用

因为指针是可以作为函数参数的,所以函数指针也可以作为函数参数,函数指针虽然复杂一点,但还是指针,因此作为函数参数可以

for  循环里面调用函数,具体调用哪个函数不知道,我们只知道调用这一款类型的函数,int (int ,int )返回int 类型  有2 个int 类型参数,

 这个  calculate 函数实现的功能看不出来,它所要实现的功能取决于这个函数指针 指向谁

#include <stdio.h>

int add(int a, int b)
{
    return a + b;
}

int mul(int a, int b)
{
    return a * b;
}

int calculate(int a[],int len, int(*cal)(int,int))
{
    int ret  = a[0];
    int i = 0;
    
    for(i=1;i<len;i++)
    {
        ret = cal(ret, a[i]);   //等价于  ret = add(ret, a[i]); 1.ret = add(a[0],a[1])
                               //       2.ret = add (ret, a[2]) ==> a[0] + a[1] + a[2]  
                               //      3.   ...n. ret =       
    }

    return ret;
}

int main()
{
    int a[] = {1,2,3,4,5};
    
    printf("1 + ... + 5 = %d\n", calculate(a, 5, add));
    printf("1 + ... + 5 = %d\n", calculate(a, 5, mul));

    return 0;
        
}

 


如果我们定义一个函数,函数的参数是一个数组,那么最终会发生退化,这个数组参数会发生退化,退化成什么?退化成指针,也就是没有数组参数一说,即便硬是要写成这样的数组参数 ,

(int a[1])  ,(int a[10]) ,(int a[100])  最终都统一的退化成这样一个指针参数  ( int* a )     

所以我们使用数组作为实参来调用函数时,没法拿到数组实参的长度信息,因为没有真正意义上的数组形参,只有指针,调用时传递的是 0 号元素的地址

       

 打印 4 出来,数组参数最后退化成指针了(int* arr),而一个指针要么 4 字节,要么 8 字节,看具体使用的系统

*arr++;   组合拳      只有指向数组的指针才能打组合拳,

数组名能不能用来打组合拳?

        不可以,这里的 a++  ==>  a = a + 1;  这个地方想要对数组名进行赋值,肯定是错误的

数组名不能用来打组合拳

这里却可以, 证明 arr[ ] 并不是数组,这个参数现在只可能是指针了

意味着数组参数退化成了一个指针

意味着这里的函数最终长成这个样子

总结 :  

函数名其实是入口地址,使用函数名调用函数,本质就是跳转到函数名所对应的入口地址处执行,并且 函数也有类型

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

长生君

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值