当函数调用的结果初始化堆栈变量时,是否复制结构?
Is a struct copied when a stack-variable is initialized by a result of a function call?
考虑到我将 return 函数中的大型结构,如下所示:
#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
标记的地方,我是否必须假设复制了这个大结构,或者编译器是否可能做了一些事情来避免这种情况?
谢谢
At the place marked by here
, do I have to assume that this large struct is copied or is it likely that the compiler does something to avoid this?
从语义上讲,结构是从函数的局部变量复制到调用者的变量。这些是不同的对象,就像其他类型的对象一样,将一个结构设置为等于另一个结构需要从一个的表示复制到另一个的表示。
避免复制的唯一方法是编译器将局部变量视为调用者结构的别名,但在一般情况下这是错误的。这种别名很容易产生明显不同的行为。
在某些特定情况下,编译器确实可以避免复制,但如果您想确保不发生复制,那么您应该明确设置所需的别名:
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 ...
}
… do I have to assume that this large struct is copied…
不,当然你不必做出那个假设。没有人要求您做出该假设,并且将该声明作为假设而不是从已知信息(例如编译器文档或检查生成的汇编代码)中推导出来是不明智的。
在您显示的特定代码中,好的编译器很可能会进行优化,以便不复制结构。 (使用 Apple Clang 11 进行的测试证实它进行了此优化。)但这可能是过于简化的代码。如果对 get_my_struct_from_file
的调用出现在与其定义分开的翻译单元中,编译器将不知道 get_my_struct_from_file
正在访问什么。如果目标对象,本例中的 res
,其地址先前已传递给其他翻译单元中的其他例程,则编译器无法知道其他例程未将地址存储在某处,并且 get_my_struct_from_file
没有使用它。因此,编译器必须将 return 由 get_my_struct_from_file
编辑的结构和 return 值分配给的结构分开对待;它无法合并它们以避免复制。
要确保编译器做你想做的事,只需告诉它你想让它做什么。编写代码,使函数将结果直接放入你想要放入的结构中:
void get_my_struct_from_file(struct my_struct *result, const char *filename)
{
…
}
...
get_my_struct_from_file(&res, "tmp.txt");
考虑到我将 return 函数中的大型结构,如下所示:
#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
标记的地方,我是否必须假设复制了这个大结构,或者编译器是否可能做了一些事情来避免这种情况?
谢谢
At the place marked by
here
, do I have to assume that this large struct is copied or is it likely that the compiler does something to avoid this?
从语义上讲,结构是从函数的局部变量复制到调用者的变量。这些是不同的对象,就像其他类型的对象一样,将一个结构设置为等于另一个结构需要从一个的表示复制到另一个的表示。
避免复制的唯一方法是编译器将局部变量视为调用者结构的别名,但在一般情况下这是错误的。这种别名很容易产生明显不同的行为。
在某些特定情况下,编译器确实可以避免复制,但如果您想确保不发生复制,那么您应该明确设置所需的别名:
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 ...
}
… do I have to assume that this large struct is copied…
不,当然你不必做出那个假设。没有人要求您做出该假设,并且将该声明作为假设而不是从已知信息(例如编译器文档或检查生成的汇编代码)中推导出来是不明智的。
在您显示的特定代码中,好的编译器很可能会进行优化,以便不复制结构。 (使用 Apple Clang 11 进行的测试证实它进行了此优化。)但这可能是过于简化的代码。如果对 get_my_struct_from_file
的调用出现在与其定义分开的翻译单元中,编译器将不知道 get_my_struct_from_file
正在访问什么。如果目标对象,本例中的 res
,其地址先前已传递给其他翻译单元中的其他例程,则编译器无法知道其他例程未将地址存储在某处,并且 get_my_struct_from_file
没有使用它。因此,编译器必须将 return 由 get_my_struct_from_file
编辑的结构和 return 值分配给的结构分开对待;它无法合并它们以避免复制。
要确保编译器做你想做的事,只需告诉它你想让它做什么。编写代码,使函数将结果直接放入你想要放入的结构中:
void get_my_struct_from_file(struct my_struct *result, const char *filename)
{
…
}
...
get_my_struct_from_file(&res, "tmp.txt");