Return 函数值

Return value of function

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

假设变量 "return_value" 在函数 foo() 中被声明为非静态(普通无符号整型)。 在函数 foo() 的末尾,此值被 returned 为

return return_value

这个值应该已经从堆栈内存中释放。 因此,接收此 return 值的主函数如何获得由 foo() 编辑的正确值 return?

除了一个例外,当一个函数 return 是一个值时,该值被 return 写入机器的 寄存器之一 (或等效物) .到函数 returning 时,函数的本地存储(堆栈框架)不再需要存储 return 值;如有必要,return 值会从局部变量复制到 return 寄存器中。

return 指针的函数除外。如果一个函数 return 是一个指针,并且它决定 return 的指针值指向本地(堆栈帧)数据,那将是一个大问题,因为到时候函数已经 return ed,该内存不再有效,因此 returned 指针值无用。

示例:

函数 returning 一个简单的常量值:

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

函数return局部变量的值:

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

函数return指针:

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

函数return字符串:

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

如果您尝试 return 指向本地存储的指针,现代编译器会警告您,如 f3()f4().

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

例如,假设你写

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() 是如何行不通的),但事实证明它 100% 完全没问题。

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

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

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);

但关键是你不必那样写;编译器会在你背后悄悄地为你做这一切。

对于整型、浮点型、指针等简单的小类型,返回值在machine rregister中。

对于结构等复杂类型,调用者将space保留在栈上,被调用者将结果存储在那里。由于调用者负责释放框架,因此由调用者决定何时处理结果。

一个注意事项:即使结构可以返回到寄存器 ss 中,只要它适合。但这取决于平台和 ABI 规范。