除了函数参数的传递之外,函数与调用方的另一个交互方式就是返回值。
在返回不同字节大小的返回值编译器的处理方式不一样:
小于4个字节:
函数将返回值存储在eax寄存器中,返回调用方之后在读取eax寄存器的值
大于4字节小于等于8字节:
函数返回值通过两个寄存器,eax和edx存储返回后读取。
大于8字节的返回值:
将利用临时对象进行返回值的传递
1、首先在mian函数中的栈上开辟一片额外的空间作为临时对象
2、调用函数时将该临时对象的地址通过隐藏的参数传递给函数
3、函数内部将返回值的数据拷贝到临时对象,并将临时对象的地址通过寄存器eax传出。
4函数返回后,将eax指向的临时对象的内存拷贝到main函数中的变量
注意:在返回自定义类型时,无论自定义类型的大小是多少都是通过临时量进行传递的
typedef struct
{
int value;
}Data;
Data GetData()
{
Data data = {10};
return data;//返回一个局部变量的值,通过寄存器拷贝
}
Data* GetDataPtr()
{
Data data = {10};
return &data;//返回局部变量的地址
}
Data& GetDataRef()
{
Data data = {10};
return data;//返回局部变量的引用,实际上是返回局部变量的地址
}
int main()
{
Data d1 = GetData();
//寄存器的值直接赋值到d1
Data &d2 = GetData();
//返回自定义类型值无论字节大小就会产生一个临时量作为引用,正确。
Data *d3 = &GetData();
//因为自定义类型返回值是拷贝到临时量,所以可以对临时量取地址。
//但是不安全,临时量会被销毁
Data *d4 = GetDataPtr();
//返回指针是四字节,直接通过寄存器带出来的是常量数字
Data * const &d5 = GetDataPtr();
//返回的是指针只是一个常量。返回指针与类型无关都是4字节
//返回指针和引用没有区别,都是通过寄存器带出来的常量数字
Data d6 = GetDataRef();
Data &d7 = GetDataRef();
Data *d8 = &GetDataRef();
return 0;
}
int GetInt()//返回值
{
int value=10;
return value;
}
int* GetIntPtr()//返回地址
{
int value = 10;
return &value;
}
int& GetIntRef()//返回引用
{
int value = 10;
return value;
}
int mai1n()
{
//返回常量值
int ret1 = GetInt();
//直接通过寄存器传出
const int &ret2 = GetInt();
//通过寄存器传出的常量必须使用常引用
//返回地址
int *p1 = GetIntPtr();
int *const& p2 = GetIntPtr();
//返回引用
int ret4 = GetIntRef();
int &ret5 = GetIntRef();
int *pret6 = &GetIntRef();//返回引用的时候直接自动解引用,可以直接替换。
return 0;
}
2580

被折叠的 条评论
为什么被折叠?



