按值返回堆栈分配的结构是否安全?

时间:2018-04-18 22:14:46

标签: c c99

[注意:这是从https://softwareengineering.stackexchange.com/q/369604/126197重新发布的,由于某种原因,这个问题立即被低估了。两次。显然,在这里有更多的爱!]

这里有一些代码从供应商的例子中解释过。

我已经找了关于按值传递堆栈分配结构的权威文档,但是还没找到确定的单词。简而言之:C99是否保证这是安全的?

typedef struct {
    int32_t upper;
    int32_t lower;
} boundaries_t;

static boundaries_t calibrate() {
    boundaries_t boundaries;         // struct allocated on stack

    boundaries.upper = getUpper();
    boundaries.lower = getLower();
    return boundaries;               // return struct by value
}

int main() {
    boundaries_t b;

    b = calibrate();
    // do stuff with b
    ...
}

请注意calibrate()在堆栈上分配boundaries结构,然后按值返回。

如果编译器可以保证calibrate()的堆栈帧在分配给b时完好无损,那么一切都很好。也许这是C99的价值合约中的合同的一部分?

(上下文:我的世界是很少看到pass-by-value的嵌入式系统。我知道从堆栈分配的结构返回一个指针是灾难的一个方法,但是这个传值东西感觉很陌生。)

3 个答案:

答案 0 :(得分:7)

是的,它非常安全。按值返回时,它会将结构成员复制到调用者的结构中。只要结构不包含任何指向本地对象的指针,它就是有效的。

返回结构往往不常见,因为如果它们变大,则需要大量复制。但有时我们将数组放入结构中以允许它们通过值传递和返回(数组通常在用作参数时返回指针或返回值),就像其他数据类型一样。

原始提问者的附录

(我相信@Barmar不会介意......)

正如@DanielH指出的那样,在SysV ABI for amd64的情况下,编译器将提供按值返回结构的规定。如果它很小,整个结构可以在寄存器中返回(读取:快速)。如果它更大,编译器在调用者的堆栈帧中分配空间并将指针传递给被调用者。然后被调用者在返回时将结构的值复制到该结构中。来自doc:

  

如果类型具有类MEMORY,则调用者为该类提供空间   返回值并在%rdi中传递此存储的地址,就像它一样   是该函数的第一个参数。实际上,这个地址   成为“隐藏的”第一个论点。

答案 1 :(得分:1)

b = calibrate();
// do stuff with b

表现得很好。

boundaries_t仅包含作为成员的整数类型。按值传递并使用在函数调用中分配给它的对象是完全安全的。

答案 2 :(得分:0)

我没有指向C99参考的链接,但引起我注意的是结构分配。

Assign one struct to another in C

这基本上是Barmar的回应。