GCC vs Clang复制struct flexible数组成员

请考虑以下代码段.

#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的初始化是否在同一个编译单元中可见.

点赞