1.函数中普通变量的内存分配问题
- 当一个函数进行调用时,函数的形参、以及函数的局部变量都会在栈中被分配内存,而栈又分两种;
- 栈低不变,栈顶不断动态变化;
- 栈顶不变,栈低在动态变化;
#include <iostream>
using namespace std;
void print()
{
int a=0,b=1,c=2;
cout<<"&a="<<reinterpret_cat<void*>(&a)<<endl;
cout<<"&b="<<reinterpret_cat<void*>(&b)<<endl;
cout<<"&c="<<reinterpret_cat<void*>(&c)<<endl;
}//reinterpret_cast运算符是用来处理无关类型之间的转换,它会产生一个新的值,这个值会有和原始参数有完全相同的比特位;
int main()
{
print();
return 0;
}
输出:
&a=0x7ffdded7a38c
&b=0x7ffdded7a390
&c=0x7ffdded7a394
- 可以发现,abc地址依次升高,所以,在C++中函数调用过程中定义的局部变量是采用栈底不变,栈顶不断变换的内存栈 ,随着声明顺序,内存地址依次降低;
2.函数中数组变量的内存分配问题
- 数组在分配时一次性分配整个数组的内存?
- 数组中各个元素的地址分配顺序时怎样的?
#include <iostream>
using namespace std;
void print()
{
int a=0;int b=1;
int c=2;int arr[2];
cout<<"&a="<<reinterpret_cast<void*>(&a)<<endl;
cout<<"&b="<<reinterpret_cast<void*>(&b)<<endl;
cout<<"&c="<<reinterpret_cast<void*>(&c)<<endl;
cout<<"&arr[0]="<<reinterpret_cast<void*>(&arr)<<endl;
cout<<"&arr[1]"<<reinterpret_cast<void*>(&arr[1])<<endl;
}
int main()
{
print();
return 0;
}
输出:
&a=0x7fffb69b53e4
&b=0x7fffb69b53e8
&c=0x7fffb69b53ec
&arr[0]=0x7fffb69b53f0
&arr[1]0x7fffb69b53f4
3.函数调用堆栈的过程
#include<stdio.h>
int sum(int a,int b)
{
int tmp=0;
tmp =a+b;
return tmp;
}
int main()
{
int a=10;
int b=20;
int ret=0;
ret=sum(a,b);
printf("ret=%d\n",ret);
return 0;
}
- 生成反汇编代码方式
1.生成代码汇编后的二进制文件;<-- gcc -c -o sum.o
2生成反汇编代码 : < ------ objdumo -d sum.o > sum.o.txt - 反汇编可执行二进制文件
gcc sum.c -g -o sum //编译
objdump -S sum > sum.txt //将在汇编代码中加入源文件代码
时间紧张,日后补上,这里就看一下结论;
- 整个函数的调用过程:
- 将调用方法的栈底地址入栈。——>push ebp;
- 让原本指向调用方栈低的ebp指向当前函数的栈底, ----> mov ebp,esp
- 给当前函数开辟栈帧;----> sub esp,44h
- 对开辟的栈帧进行初始化,初始化的大小不一定; ----->rep stos
- 结论:
1.函数的运行都是在栈上开辟内存的;
2.栈是通过esp(栈顶指针)、ebp(栈底指针)两个指针来标识的;
3.对于栈上的访问都是通过栈底指针的偏移来访问的;
4.在call一个函数时,有两件事要做:先将调用的函数的下一行指令的地址压人栈中;再进行跳转;
5.在函数调用时检查函数是否申明、函数名是否相同、函数的参数列表是否匹配、函数的返回值多大;
(1)如果函数返回值<=4个字节,则返回值通过寄存器eax带回;
(2)如果4<函数返回值<=8个字节,则返回值通过两个寄存器eax和edx带回,
(3)如果函数返回值>8个字节,则返回值同产生的临时量带回;
6.函数结束ret指令干了两件事:先出栈;再将出栈的值放到CPU的PC寄存器中,因为PC寄存器中永远存放的是下一次指令的地址;