Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pointer arithmetics and comparison within a structure

We have:

struct A {
  int x;
  int y;
} a;

Assuming that:

offsetof(struct A, x) + sizeof(int) == offsetof(struct A, y)

Does the C standard (i.e. C11) guarantee that &a.x + 1 == &a.y is true?

If not, does any mainstream compiler guarantee that?

Moreover, assuming that the equality is satisfied, can the value of a.y be accessed via (&a.x)[1] without UB?

What about memcpy(&a.x+1, ...)?


EDIT

Accessing a.y with (&a.x)[1] is indeed UB, at least for CLANG. See example by user @NateEldredge.

like image 456
tstanisl Avatar asked Sep 05 '21 20:09

tstanisl


1 Answers

Yes. C 2017 6.5.9, which discusses == and !=, says:

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.

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.

By paragraph 7, a.x and a.y each acts as an array of one int, for the purposes of ==.

Since offsetof(struct A, x) + sizeof(int) == offsetof(struct A, y) guarantees that a.y immediately follows a.x in the address space, &a.x+1 == &a.y satisfies the last condition in paragraph 6, that one is a pointer to one past the end of one array object and the other is a pointer to a different array object that immediately follows the first.

Moreover, assuming that the equality is satisfied, can the value of a.y be accessed via (&a.x)[1] without UB?

No. The fact that &a.x+1 equals &a.y does not mean it is &a.y.

This is not fully or explicitly stated in the standard. There are situations where pointer arithmetic must be able to traverse objects that are adjacent in memory, particularly structure members. For example, if we convert a pointer to a structure to a char *, we can use it to access the individual bytes in the structure. We can use it to traverse the entire structure, including the members. Then, if we increment it appropriately to point to some member and convert it back to a pointer to that member’s type, we ought to have a pointer to that member.

However, the C standard is written in natural language, not completely with notation of formal logic or mathematics (although there is some), so it is incomplete, and we are not always sure of what it specifies. Since it does not tell us that &a.x + 1 can be used to access a.y, the behavior is undefined by omission.

like image 69
Eric Postpischil Avatar answered Sep 17 '22 14:09

Eric Postpischil