函数的返回值

每个函数调用都在堆栈上执行,并且在函数退出后从堆栈内存中释放该函数。

假设变量“return_value”在函数foo()中声明为非静态(normal unsigned int)。 在函数foo()的末尾,该值返回为

return return_value 

该值应该已从堆栈内存中释放。 因此,接收此返回值的main函数如何获得foo()返回的正确值?

除了一个例外,当函数返回一个值时,该值将在一个机器的寄存器 (或等效的寄存器 )中返回。 当函数返回时,不再需要函数的本地存储(堆栈帧)来存储返回值; 如有必要,返回值将从局部变量复制到返回寄存器中。

例外是返回指针的函数。 如果一个函数返回一个指针,并且如果指针值决定将点返回到本地(堆栈帧)数据,这是一个大问题,因为在函数返回时,该内存不再是真正有效的,并且所以返回的指针值是无用的。

例子:

函数返回一个简单的常量值:

 int f1() { return 4; /* constant value 4 copied to return register */ } 

函数返回局部变量的值:

 int f2() { int i = 5; return i; /* value copied from i to return register */ } 

返回指针的函数:

 int *f3() { int i = 5; return &i; /* WRONG: returns pointer to local storage */ } 

函数返回一个字符串:

 char *f4() { char str[] = "Hello, world!"; return str; /* WRONG: returns pointer to local storage */ } 

如果您尝试返回指向本地存储的指针,现代编译器将发出警告,如f3()f4()

实际上,当涉及返回结构值的函数时,还有另一个例外。 结构可以任意大,因此结构返回值不一定适合任何机器的寄存器。 但是C语言定义说你可以从函数返回结构值并让它正常工作,所以编译器必须为你做一些额外的工作,以确保有一些安全的地方将返回值传回给调用者。

例如,假设你写

 struct person { char firstname[100]; char lastname[100]; }; struct person f5() { struct person ret; strcpy(ret.firstname, "Akshay"); strcpy(ret.lastname, "Immanuel"); return ret; } 

这可能看起来很危险(特别是如果你还记得前面的例子f4()不起作用的话),但事实certificate它是完全没问题的。

[脚注。 实际上,当然,我的示例以不同的方式存在危险,因为当使用strcpy将字符串复制到firstnamelastname时,它不会检查溢出。

假设您从其他地方调用此函数:

 struct person x; x = f(); 

这是如何运作的? 嗯,这取决于编译器; 不同的编译器以不同的方式做。 一种方法是编译器基本上假装你以不同的方式编写了函数。 它假装你写了

 void f5(struct person *retp) { struct person ret; strcpy(ret.firstname, "Akshay"); strcpy(ret.lastname, "Immanuel"); *retp = ret; } 

然后当你打电话时,它假装你已经写完了

 struct person x; f5(&x); 

但重点是你不必那样写; 编译器在你背后无形地为你完成所有这些。

对于简单的小类型,如整数,浮点类型和指针,该值在机器rregister中返回。

对于诸如结构之类的复杂类型,调用者保留堆栈上的空间,并且被调用者将结果存储在那里。 由于调用者负责释放帧,因此在处理结果时由它决定。

需要注意的是:偶数结构可以在寄存器中返回,只要它适合。 但这取决于平台和ABI规范。