函数的返回值

Aks*_*l D 1 c embedded

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

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

return return_value
Run Code Online (Sandbox Code Playgroud)

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

Ste*_*mit 8

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

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

例子:

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

int f1() {
    return 4;        /* constant value 4 copied to return register */
}
Run Code Online (Sandbox Code Playgroud)

函数返回局部变量的值:

int f2() {
    int i = 5;
    return i;        /* value copied from i to return register */
}
Run Code Online (Sandbox Code Playgroud)

返回指针的函数:

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

函数返回一个字符串:

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

如果您尝试返回指向本地存储的指针,现代编译器将警告您,如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;
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

struct person x;
x = f();
Run Code Online (Sandbox Code Playgroud)

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

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

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

struct person x;
f5(&x);
Run Code Online (Sandbox Code Playgroud)

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