C语言中函数指针、回调函数及指针函数

一、 函数名

函数名是一个函数的入口地址(一个常量指针),函数本质上是一段代码,存放在内存中的代码段(.text段),函数名就是函数在代码段的入口地址。
调用函数的时候,就是跳转到该地址,调用函数。
【注意】:函数名和&函数名的地址是一样的,它们的机制是一摸一样的,和数组有本质区别。

#include <stdio.h>

int main(int argc, char const *argv[])
{
printf("main      = %p\n", main);
    printf("main + 1  = %p\n", main + 1);

    printf("&main     = %p\n", &main);
    printf("&main + 1 = %p\n", &main + 1);

    return 0;
}

运行结果:
在这里插入图片描述

二、函数指针(实质上是一个指针)

1.一般形式

函数的返回值类型 (* 函数指针变量名)(形参列表);
代码分析:

#include <stdio.h>

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

int main(int argc, char const *argv[])
{
    int (* pAdd)(int, int) = Add;//定义函数指针的时候初始化,也可以先定义后初始化

    printf("%d\n", pAdd(1, 2));

return 0;
}

运行结果:
在这里插入图片描述

2.自定义形式

typedef 函数的返回值类型 (* 函数指针变量的数据类型)(形参列表);
代码分析:

#include <stdio.h>

typedef int (* pFUNC_Add)(int, int);

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

int main(int argc, char const *argv[])
{
    pFUNC_Add pAdd = Add;   //定义函数指针**加粗样式**的时候初始化
	
	//**函数调用的三种方法**
	//(1)使用函数名调用,本质上是地址的跳转
	//(2)函数指针调用,函数指针存放函数的入口地址
	//(3)(* 函数指针)调用,间接引用函数入口地址
printf("%d\n", pAdd(1, 2));
printf("%d\n", Add(1, 2));
printf("%d\n", (* pAdd)(1, 2));

    return 0;
}

运行结果:
在这里插入图片描述

3.函数指针的作用

调用函数和做函数的参数

4.注意

函数指针声明为指针,它与变量指针不同之处是,它不是指向变量,而是指向函数
(1)函数指针的实质还是指针,还是指针变量。本身占8字节(在64位系统中,所有的指针都是8字节)
(2)函数指针、数组指针、普通指针之间并没有本质区别,区别在于指针指向的东西是个什么东西。
(3)函数的实质是一段代码,这一段代码在内存中是连续分布的(一个函数的大括号括起来的所有语句将来编译出来生成的可执行程序是连续的)。所以对于函数来说,很关键的就是函数中的第一句代码的地址,这个地址就是所谓的函数地址,在C语言中用函数名这个符号来表示。
(4)结合函数的实质,函数指针其实就是一个普通变量,这个普通变量的类型是函数指针变量类型,它的值就是某个函数的地址(也就是它的函数名这个符号在编译器中对应的地址值)。

三、 回调函数

1.基本概念

向一个函数传递函数指针,使一个函数可以调用另一个函数,叫做回调函数
代码分析:

#include <stdio.h>

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

//回调函数add,通过函数指针调用add_sum函数
int add(int a, int b, int (* padd_sum)(int a, int b))
{
    return padd_sum(a, b);
}

int main(int argc, char const *argv[])
{
    int sum = add(1, 2, add_sum);   //函数名即函数指针作为函数的参数

    printf("sum = %d\n", sum);

    return 0;
}

运行结果:3

2.应用实例

通过上面代码分析,谈论开发者和用户之间的关系。假设用户实现的是add函数,而开发者实现的是add_sum函数。现在需求是,用户将add_sum函数以函数参数的形式传给开发者实现的add函数,add函数就会返回一个值给用户。此时,开发者并不需要告诉用户他具体实现了什么,用户也不知道开发者是怎么实现的。用户只需要传入自己的函数,便可以得到开发者实现的函数的返回值,开发者可以将内容封装起来,将头文件和库文件提供给用户。

a.我们在一目录下,创建3个文件main.c、add.c、add.h。
main.c是用户开发实现的
add.c和add.h是开发者实现的
main.c

#include <stdio.h>
#include "add.h"

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

int main(int argc, char const *argv[])
{
    int sum = add(1, 2, add_sum);

    printf("sum = %d\n", sum);

    return 0;
}

add.h

#ifndef _ADD_H_
#define _ADD_H_

int add(int, int, int (*)(int, int));

#endif

add.c

#include "add.h"

int add(int a, int b, int (* padd_sum)(int a, int b))
{
    return padd_sum(a, b);
}

b.制作一个动态链接库,开发者把add.c的内容封装起来,把add.h提供给用户使用。

#include <stdio.h>
#include "add.h"

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

int main(int argc, char const *argv[])
{
    int sum = add(1, 2, add_sum);

    printf("sum = %d\n", sum);

    return 0;
}

在这里插入图片描述
在linux下制作动态链接库,将add.c和add.h打包成动态链接库。

  1. 执行gcc -shared -fPIC -o libadd.so,在当前路径生成一个动态库文件libadd.so
    -shared 生成动态库
    -fPIC 生成与位置无关的代码
    -o 指定生成的目标文件
  2. 执行sudo gcc main.c -L . -ladd -o main
    -L 指定库的径(编译时),不指定的话,使用默认路径(/usr/lib/lib)
    -ladd 指定需要的动态库是谁
    【注意】因为我是普用户,无法在系统目录下创建文件,使用sudo 暂时性的提升用户等级去创建文件在系统目录下。
  3. 执行./main,加载动态库(默认加载路径是/usr/lib/lib./…)
    我们将编译的动态库生成的libadd.so 拷贝到/usr/lib后,就可以不需要add.c了,此时我们将add.c移除,也可以正常编译并且执行main函数的结果,这就是回调函数的应用之一。
    在这里插入图片描述
    参考:添加链接描述

3.回调函数在内核中应用

学习内核知识后补充

四、 指针函数(实质上是一个函数)

1.基本概念

指针函数实质上是一个函数,该指针函数的返回值类型是某一类型的指针,如果不返回值,则无返回值类型函数。

2.定义的一般形式

函数的返回值类型 *函数名(形参列表)

include <stdio.h>

//下面是指针函数的定义
//指针函数的返回值char *的指针变量,该指针指向字符串"Hello World!"
char *func(void)     
{
    char *s = "Hello World!";
    return s;
}

int main(int argc, char const *argv[])
{
    char *p;

    p = func();        //指针函数的调用
    printf("%s\n", p);

    return 0;
}

运行结果:Hello World!

3.指针函数的作用

(1) 可以使代码更简洁

(2) 节省存储空间的使用

如果需要返回一个数组的元素,只需返回数组首元素的地址给调用函数,调用函数就可以访问该数组。或者是malloc动态分配内存,返回该内存的地址给另一个函数,另一个函数就可以操作该动态内存,使用完动态内存一定要释放,防止产生内存泄漏。
【注意】指针函数本质是一个函数,使用规则和普通函数并无差异。函数需要声明、定义以及调用,而变量需要定义、赋值以及调用(如函数指针),两者有明显区别。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值