请考虑以下代码段.
#include <stdio.h>
typedef struct s {
int _;
char str[];
} s;
s first = { 0, "abcd" };
int main(int argc, const char **argv) {
s second = first;
printf("%s\n%s\n", first.str, second.str);
}
当我用GCC 7.2编译它时,我得到:
$gcc-7 -o tmp tmp.c && ./tmp
abcd
abcd
但是当我用Clang(Apple LLVM版本8.0.0(clang-800.0.42.1))编译它时,我得到以下内容:
$clang -o tmp tmp.c && ./tmp
abcd
# Nothing here
为什么编译器之间的输出不同?我希望字符串不被复制,因为它是一个灵活的数组成员(类似于this question).为什么GCC实际上会复制它?
编辑
一些评论和答案表明这可能是由于优化. GCC可能会成为第二个别名,因此更新第二个应该禁止GCC进行优化.我添加了这一行:
second._ = 1;
但这不会改变输出.
最佳答案 这是gcc正在发生的事情的真实答案.第二个是在堆栈上分配的,正如您所期望的那样.它不是第一个别名.通过打印地址可以轻松验证这一点.
另外,声明的第二个=第一个;正在破坏堆栈,因为(a)gcc正在为第二个分配最小的存储量,但(b)它将所有的第一个复制到第二个,从而破坏堆栈.
这是原始代码的修改版本,显示了以下内容:
#include <stdio.h>
typedef struct s {
int _;
char str[];
} s;
s first = { 0, "abcdefgh" };
int main(int argc, const char **argv) {
char v[] = "xxxxxxxx";
s second = first;
printf("%p %p %p\n", (void *) v, (void *) &first, (void *) &second);
printf("<%s> <%s> <%s>\n", v, first.str, second.str);
}
在我的32位Linux机器上,使用gcc,我得到以下输出:
0xbf89a303 0x804a020 0xbf89a2fc
<defgh> <abcdefgh> <abcdefgh>
从地址中可以看出,v和second位于堆栈中,并且首先位于数据部分.此外,还清楚的是,第二次初始化已经覆盖了堆栈上的v,结果是代替预期的< xxxxxxxx>,而是显示< defgh>.
这对我来说似乎是一个gcc bug.至少,它应警告第二个的初始化将破坏堆栈,因为它显然有足够的信息在编译时知道这一点.
编辑:我对此进行了更多测试,并通过将第二个声明拆分为以下内容获得了基本相同的结果:
s second;
second = first;
真正的问题是任务.它首先复制所有内容,而不是结构类型的最小公共部分,这是我认为它应该做的.实际上,如果将第一个静态初始化移动到一个单独的文件中,则赋值执行它应该执行的操作,v打印正确,而second.str是未定义的垃圾.这是gcc应该生成的行为,无论first的初始化是否在同一个编译单元中可见.