【发布时间】:2018-06-03 07:50:23
【问题描述】:
具体来说,是否允许两个自动变量在不同函数中的地址比较相等,如下:
sink.c
#include <stdio.h>
#include <stdlib.h>
void sink(void *l, void *r) {
puts(l == r ? "equal" : "not equal");
exit(0);
}
main.c
typedef struct { char x[32]; } Foo;
void sink(void *l, void *r);
Foo make(void *p) {
Foo f2;
sink(&f2, p);
return f2;
}
int main() {
Foo f1 = make(&f1);
}
我希望这会打印not equal,因为f1 和f2 是不同的对象。使用 gcc 我得到 not equal,但使用本地版本的 clang 3.81,当编译为 clang -O1 sink.c main.c2 时,它会打印 equal。
拆解make和main ...
0000000000400570 <make>:
400570: 53 push rbx
400571: 48 89 fb mov rbx,rdi
400574: e8 d7 ff ff ff call 400550 <sink>
400579: 48 89 d8 mov rax,rbx
40057c: 5b pop rbx
40057d: c3 ret
40057e: 66 90 xchg ax,ax
0000000000400580 <main>:
400580: 48 83 ec 28 sub rsp,0x28
400584: 48 8d 7c 24 08 lea rdi,[rsp+0x8]
400589: 48 89 fe mov rsi,rdi
40058c: e8 df ff ff ff call 400570 <make>
400591: 31 c0 xor eax,eax
400593: 48 83 c4 28 add rsp,0x28
400597: c3 ret
...我们看到make 似乎根本没有创建Foo f2 对象,它只是使用现有的rdi 和rsi(l 和r 参数调用sink , 分别)。这些由main 传递并且是相同的:第一个rdi 是指向放置返回值的位置的隐藏指针,第二个是&f1,所以我们希望它们是相同的。
1我检查了最高 7.0 的版本,行为大致相同。
2 它发生在-O1、-O2 和-O3,但不是-O0,而是打印not equal。
【问题讨论】:
-
@r3musn0x on the [c] 标签问题是关于标准 C 的,除非另有说明
-
我在问语言是否允许这样做,就像大多数“X 可以发生”的问题一样。在我的情况下,指针是否比较相等尚不清楚:也许我的程序中潜伏着一些 UB,它可以让编译器做它想做的任何事情。我想我本可以完全排除 clang 行为,但编译器行为通常可以作为标准允许的一个很好的积极测试。 @r3
-
编译器错误。过分热心的优化器。结果必须是“不等于”。
-
@RbMm - 那是 C++,但这个问题是关于 C 的。在 C++ 中,规则是不同的,尤其是 RVO。使用 Godbolt 中的下拉菜单选择 C。