Section 6.5.9 of the C standard regarding the ==
and !=
operators states the following:
2 One of the following shall hold:
- both operands have arithmetic type;
- both operands are pointers to qualified or unqualified versions of compatible types;
- one operand is a pointer to an object type and the other is a pointer to a qualified or unqualified version of void; or
- one operand is a pointer and the other is a null pointer constant.
...
6 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.109)
7 For the purposes of these operators, a pointer to an object that is
not an element of an array behaves the same as a pointer to the first
element of an array of length one with the type of the object as its
element type.
Footnote 109:
109) Two objects may be adjacent in memory because they are adjacent
elements of a larger array or adjacent members of a structure with no
padding between them, or because the implementation chose to place
them so, even though they are unrelated. If prior invalid pointer
operations (such as accesses outside array bounds) produced undefined
behavior, subsequent comparisons also produce undefined behavior.
This would seem to indicate you could do the following:
int a;
int b;
printf("a precedes b: %d
", (&a + 1) == &b);
printf("b precedes a: %d
", (&b + 1) == &a);
This should be legal since we are using an address one element past the end of an array (which in this case is a single object treated as an array of size 1) without dereferencing it. More importantly, one of these two statements would be required to output 1
if one variable immediately followed the other in memory.
However, testing didn't seem to pan this out. Given the following test program:
#include <stdio.h>
struct s {
int a;
int b;
};
int main()
{
int a;
int b;
int *x = &a;
int *y = &b;
printf("sizeof(int)=%zu
", sizeof(int));
printf("&a=%p
", (void *)&a);
printf("&b=%p
", (void *)&b);
printf("x=%p
", (void *)x);
printf("y=%p
", (void *)y);
printf("addr: a precedes b: %d
", ((&a)+1) == &b);
printf("addr: b precedes a: %d
", &a == ((&b)+1));
printf("pntr: a precedes b: %d
", (x+1) == y);
printf("pntr: b precedes a: %d
", x == (y+1));
printf(" x=%p, &a=%p
", (void *)(x), (void *)(&a));
printf("y+1=%p, &b+1=%p
", (void *)(y+1), (void *)(&b+1));
struct s s1;
x=&s1.a;
y=&s1.b;
printf("addr: s.a precedes s.b: %d
", ((&s1.a)+1) == &s1.b);
printf("pntr: s.a precedes s.b: %d
", (x+1) == y);
return 0;
}
Compiler is gcc 4.8.5, system is CentOS 7.2 x64.
With -O0
, I get the following output:
sizeof(int)=4
&a=0x7ffe9498183c
&b=0x7ffe94981838
x=0x7ffe9498183c
y=0x7ffe94981838
addr: a precedes b: 0
addr: b precedes a: 0
pntr: a precedes b: 0
pntr: b precedes a: 1
x=0x7ffe9498183c, &a=0x7ffe9498183c
y+1=0x7ffe9498183c, &b+1=0x7ffe9498183c
addr: s.a precedes s.b: 1
We can see here that an int
is 4 bytes and that the address of a
is 4 bytes past the address of b
, and that x
holds the address of a
while y
holds the address of b
. However the comparison &a == ((&b)+1)
evaluates to false while the comparison (x+1) == y
evaluates to true. I would expect both to be true as the addresses being compared appear identical.
With -O1
, I get this:
sizeof(int)=4
&a=0x7ffca96e30ec
&b=0x7ffca96e30e8
x=0x7ffca96e30ec
y=0x7ffca96e30e8
addr: a precedes b: 0
addr: b precedes a: 0
pntr: a precedes b: 0
pntr: b precedes a: 0
x=0x7ffca96e30ec, &a=0x7ffca96e30ec
y+1=0x7ffca96e30ec, &b+1=0x7ffca96e30ec
addr: s.a precedes s.b: 1
pntr: s.a precedes s.b: 1
Now both comparisons evaluate to false even though (as before) the address being compared appear to be the same.
This seems to point to undefined behavior, but based on how I read the above passage it seems this should be allowed.
Note also that the comparison of the addresses of adjacent objects of the same type in a struct
prints the expected result in all cases.
Am I misreading something here regarding what is allowed (meaning this is UB), or is this version of gcc non-conforming in this case?
See Question&Answers more detail:
os