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.c2.
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