Home > Blockchain >  pointer arithmetics and comparison within a structure
pointer arithmetics and comparison within a structure

Time:09-17

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?


Edit

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

CodePudding user response:

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.

CodePudding user response:

May be related to your example: ISO/IEC 9899:2018 (C17) at 6.7.2.1#21 talks about accessing an unspecified-size array at the end of a struct:

struct s {
  int n;
  double d[];
};

// given this assumption:
assert( sizeof (struct s) >= offsetof(struct s, d)   sizeof (double) );

// the standard says that the following code may be legitimate and might not be UB,
// but it is NOT a strictly conforming code:
struct s t1;
t1.d[0] = 4.2;

The previous example is very similar to yours. So my guess is: what you want to do may not be UB (given you used offsetof to check etc.), but your code is not strictly conforming.

  • Related