In particular, is it allowed for the addresses of two automatic variables in different functions to compare equal as follows:
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);
}
I would expect this to print not equal
as f1
and f2
are distinct objects. With gcc I get not equal
, but with my local version of clang 3.81, it prints equal
, when compiled as clang -O1 sink.c main.c
2.
Disassembling make
and 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
... we see that make
never seems to create the Foo f2
object at all, it just calls sink
with the existing rdi
and rsi
(the l
and r
parameters, respectively). These are passed by main
and are the same: the first, rdi
, is the hidden pointer to the location to put the return value, and the second is &f1
, so we expect these to be the same.
1 I checked versions up to 7.0 and the behavior is roughly the same.
2 It happens for -O1
, -O2
and -O3
, but not -O0
which prints not equal
instead.
The C11 standard part 6.5.9/6 says:
Two pointers compare equal if and only if both are null pointers, both are pointers to the same object (including a pointer to an object and a subobject at its beginning) or function, both are pointers to one past the last element of the same array object, or one is a pointer to one past the end of one array object and the other is a pointer to the start of a different array object that happens to immediately follow the first array object in the address space.
In this code none of the listed conditions hold; &f1
and &f2
are pointers to different objects, and one is not a subobject of the other.
So the pointers must not compare equal. The compiler reporting equal
is non-conforming.
Note: If anyone has doubts about the legality of Foo f1 = make(&f1);
, see this question. It is fine and the automatic object's lifetime begins at the preceding {
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With