【问题标题】:Is a struct copied when a stack-variable is initialized by a result of a function call?当堆栈变量由函数调用的结果初始化时,是否复制了结构?
【发布时间】:2021-10-17 14:50:54
【问题描述】:

假设我将在函数中返回一个大型结构,如下所示:

#include <stdio.h>

// this is a large struct
struct my_struct {
   int x[64];
   int y[64];
   int z[64];
};

struct my_struct get_my_struct_from_file(const char *filename) {
    int tmp1, tmp2; // some tmp. variables
    struct my_struct u;
    // ... load values from filename ...
    return u;
}

int main() {
    struct my_struct res = get_my_struct_from_file("tmp.txt"); // <-- here
    printf("x[0] = %d\n", res.x[0]);
    // ... print all values ...
}

here 标记的地方,我是否必须假设这个大结构被复制了,还是编译器可能会采取一些措施来避免这种情况?

谢谢

【问题讨论】:

  • 唯一知道的方法是检查生成的程序集。但 NRVO 似乎是一个合理的优化,即使在 C 语言中(ABI 愿意)。
  • 您的get_my_struct_from_file 是否总是与对其的任何调用在同一个翻译单元中,或者它是否可以在外部链接单元中,例如对象库?如果是后者,那么避免完整副本似乎很棘手。

标签: c copy


【解决方案1】:

…我是否必须假设这个大结构被复制了…

不,您当然不必做出这样的假设。没有人要求您做出这样的假设,并且采用该语句作为假设而不是从已知信息(例如编译器文档或检查生成的汇编代码)中推导出它是不明智的。

在您显示的特定代码中,好的编译器很可能会优化,以便不复制结构。 (使用 Apple Clang 11 进行的测试证实了它进行了这种优化。)但这可能是过于简化的代码。如果对get_my_struct_from_file 的调用出现在与其定义不同的翻译单元中,编译器将不知道get_my_struct_from_file 正在访问什么。如果目标对象,在本例中为res,其地址先前已传递给其他翻译单元中的其他例程,则编译器无法知道其他例程没有将地址存储在某处并且get_my_struct_from_file 没有使用它。所以编译器必须将get_my_struct_from_file返回的结构和返回值分配给的结构分开处理;它无法合并它们以避免复制。

为确保编译器执行您想要的操作,只需告诉它您想要它执行的操作即可。编写代码,使函数将结果直接放入您要放入的结构中:

void get_my_struct_from_file(struct my_struct *result, const char *filename)
{
    …
}

...
    get_my_struct_from_file(&res, "tmp.txt");

【讨论】:

    【解决方案2】:

    here 标记的地方,我是否必须假设这个大结构被复制了,还是编译器可能会采取一些措施来避免这种情况?

    在语义上,结构从函数的局部变量复制到调用者的变量。这些是不同的对象,就像其他类型的对象一样,将一个结构设置为另一个结构需要从一个表示复制到另一个表示。

    避免复制的唯一方法是编译器将局部变量视为调用方结构的别名,但在一般情况下这是错误的。这种混叠很容易产生与没有混叠时明显不同的行为。

    有可能在某些特定情况下,编译器确实可以避免复制,但如果您想确保不会发生复制,那么您应该明确设置想要的别名:

    void get_my_struct_from_file(const char *filename, struct my_struct *u) {
        int tmp1, tmp2; // some tmp. variables
        // ... load values from filename into *u
    }
    
    int main() {
        struct my_struct res = { 0 };
        get_my_struct_from_file("tmp.txt", &res);
        printf("x[0] = %d\n", res.x[0]);
        // ... print all values ...
    }
    

    【讨论】:

      猜你喜欢
      • 2018-04-06
      • 2017-05-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-08-30
      • 2013-02-28
      • 1970-01-01
      相关资源
      最近更新 更多