函数调用的内存中的三个区域

1.代码区
代码区装载了这个程序所对应的机器指令
程序的执行就靠这些指令来驱动
2.静态数据区
装载了全局变量的数值
后面程序的执行会改变这里的值
3.动态数据区
初始什么都没有,因为只有程序执行后,在指令的驱动下,这个区域才会产生数据。
压栈、清栈的工作就是在这个区域完成的。

CPU的三个寄存器

1.eip
代码区装载了这个程序所对应的机器指令
程序的执行就靠这些指令来驱动
2.ebp
装载了全局变量的数值
后面程序的执行会改变这里的值
3.esp
初始什么都没有,因为只有程序执行后,在指令的驱动下,这个区域才会产生数据。
压栈、清栈的工作就是在这个区域完成的。

指针函数概念(返回值)

指针函数是 返回指针的函数 主体是函数,返回值是一个指针

int fun(int,int); //更加直观,返回值是int 类型
int fun(int,int);
int
fun(int,int);

在实现一个指针函数时,应该特别注意,指针函数返回的地址,在主调函数中,必须是有效的,是可以访问的内存。在上面程序中,str是函数内部的局部数组,局部变量分配在堆栈中,当函数执行完后,局部变量自动释放,在主调函数中,不能再访问,因此会有警告。访问一段释放的内存,是非法操作,显示的是0地址,若修改非法内存中的值,程序的后果可能更严重,是不可预料的

系统指针函数举例

字符串拷贝函数

字符串连接函数

函数指针

函数指针是专门用来存放函数地址的指针,函数地址是一个函数的入口地址,函数名代表了函数的入口地址。

当一个函数指针指向了一个函数,就可以通过这个指针来调用该函数,可以将函数作为参数传递给函数指针

声明形式

<数据类型> (*<函数指针名称>)(<参数说明列表>);

<数据类型>是函数指针所指向的函数的返回值类型;
<函数指针名称>符合标识符命名规则;
<参数说明列表>应该与函数指针所指向的函数的形参说明保持一致;
<函数指针名称>)中,说明为指针,()不可缺省,表明为指向函数的指针。

例子:int (fun) (int);
注意
和函数名要用括号括起来,否则因为运算符的优先级原因就变成指针函数了

案例

定义函数指针类型

有时为了书写方便,可以声明一个函数指针数据类型
在函数指针变量说明前面,加上typedef,就变成了函数指针类型。
typedef <数据类型> (*<函数指针类型名称>)(<参数说明列表>);

typedef int (*MFunc)(int, int);

int test(int a, int b, MFunc pFunc);
int plus(int a, int b); //函数声明
int minus(int, int); //函数声明,缺省形参名称

函数指针数组

函数指针数组是一个包含若干个函数指针变量的数组。

<数据类型> ( * <函数指针数组名称> [<大小>] ) ( <参数说明列表> );
<大小>是指函数指针数组元素的个数。

函数指针程序举例

函数回调

本质上就是一个函数指针的应用场景而已

回调函数主要结构有三部分组成:主函数、调用函数和被调函数,主要核心目的值在为了完成某个功能之后给与消息通知,或者是功能触发

因为可以把调用者与被调用者分开。调用者不关心谁是被调用者,所有它需知道的,只是存在一个具有某种特定原型、某些限制条件(如返回值为int)的被调用函数

如果想知道回调函数在实际中有什么作用,先假设有这样一种情况,我们要编写一个库,它提供了某些排序算法的实现,如冒泡排序、快速排序、归并排序等等,但为使库更加通用,不想在函数中嵌入排序逻辑,而让使用者来实现相应的逻辑;或者,想让库可用于多种数据类型(int、float、string),此时,该怎么办呢?可以使用函数指针,并进行回调

回调函数的优点

可以让实现方,根据回调方的多种形态进行不同的处理和操作。

可以让实现方,根据自己的需要定制回调方的不同形态。

可以将耗时的操作隐藏在回调方,不影响实现方其它信息的展示。

让代码的逻辑更加集中,更加易读。

总结

1.函数返回值返回是由条件的

2.函数的参数传递,数组条件下会有降级问题出现

3.函数实际上是有执行地址的

4.回调函数就是函数指针的一种应用

本文地址: http://www.yppcat.top/2023/02/19/函数指针与指针函数/