失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > 从底层来看函数的调用和返回

从底层来看函数的调用和返回

时间:2020-04-16 05:39:21

相关推荐

从底层来看函数的调用和返回

目录

一、引言二、参数和局部变量的实现2.1局部变量2.2参数---形参和实参2.3编译器是如何实现局部变量和参数的

一、引言

学c语言到现在,用了这么久的函数,可是我们为什么要使用函数呢?

其实就是为了重用一段代码。使用函数,我们可以输入不同的值,通过返回值来得到结果。

相关概念:

在汇编语言中,使用指令"call"和"ret"通过改变PC(程序计数器)的值来改变执行顺序

在这个过程中,也涉及到了数据的移动,像参数和返回值

当然程序内部,还有一些局部变量

二、参数和局部变量的实现

2.1局部变量

问题:变量可以取相同的名字吗?

有时可以,取决于情况

为什么呢?

因为编译器会将相同名字的变量映射到不同的地址。

例子

#include<stdio.h>int first;int second;//定义了一个全局的变量secondvoid callee(int first){int second;//定义了一个局部的变量secondsecond=1;first=2;printf("callee:first=%d,second=%d\n",first,second);printf("address of second:%#x\n\n",&second);}int main(){first=1;second=2;callee(first);printf("caller:first=%d,second=%d\n",first,second);printf("address of second:%#x\n",&second);return 0;}

运行结果:

callee:first=2,second=1address of second:0x61fed8caller:first=1,second=2address of second:0x405068

通过这个例子,我们发现:

1.变量占用内存

2.编译器通过将相同名称的变量映射到不同的地址以实现作用域。

2.2参数—形参和实参

首先思考一个问题,形参和实参在内存中是共用同一块地址吗?也就是说,形参和实参的地址相同吗?

答案是否定的,形参和实参的地址不同。

可以通过一个小例子测试一下:

#include<stdio.h>void callee(int formal_parameter){printf("address of second:%#x\n\n",&formal_parameter);}int main(){int actual_parameter=1;printf("address of second:%#x\n",&actual_parameter);callee(actual_parameter);//传入的实参的值return 0;}

运行结果:

address of second:0x61fefcaddress of second:0x61fee0

也就是说,编译器会为函数中的形参重新分配一个地址

那么为什么要这样设计:

这允许实际参数不受干扰,也就是说,不管我怎么修改我形参中的值,我实参都不会改变,保护了实参。

按值传递

–这允许实际参数保持不受干扰

通过引用

–C语言不支持引用调用,但是它的&和*操作符很容易达到相同的效果。相关内容可以查看C语言中指针传参

2.3编译器是如何实现局部变量和参数的

先看一个递归的例子

#include<stdio.h>void callee(int n){printf("Function calls:%d(0x%08x)\n",n,&n);if (n==0) {printf("Function returns:%d(0x%08x)\n",n,&n);return;}callee(n-1);printf("Function returns:%d(0x%08x)\n",n,&n);}int main(int argc,char *argv[]){int n=5;callee(n);return 0;}

运行结果:

Function calls:5(0x0061fee0)Function calls:4(0x0061fec0)Function calls:3(0x0061fea0)Function calls:2(0x0061fe80)Function calls:1(0x0061fe60)Function calls:0(0x0061fe40)Function returns:0(0x0061fe40)Function returns:1(0x0061fe60)Function returns:2(0x0061fe80)Function returns:3(0x0061fea0)Function returns:4(0x0061fec0)Function returns:5(0x0061fee0)

在每次调用中,都需要创建一个本地变量n。思考一个问题,编译器怎么知道这个函数需要调用多少次呢?

编译器无法在程序运行完之前确定,所以只能在运行时动态分配。

这样做的缺点就是程序运行时性能会降低。

那编译器是怎么具体实现的呢?

因为编译器不知道它需要动态地分配多少变量,所以它保留了大量的空间来进行扩展。

堆栈:

堆栈是存储内存的一个特殊区域(用来存储每个函数创建的临时变量)

随着函数Push和Pop变量,栈也相应的增长和缩小。

如果觉得《从底层来看函数的调用和返回》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。