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