Home > Blockchain >  Is it valid to calculate element pointers by explicit arithmetic?
Is it valid to calculate element pointers by explicit arithmetic?

Time:11-11

Is the following program valid? (In the sense of being well-defined by the ISO C standard, not just happening to work on a particular compiler.)

struct foo {
  int a, b, c;
};

int f(struct foo *p) {
  // should return p->c
  char *q = ((char *)p)   2 * sizeof(int);
  return *((int *)q);
}

It follows at least some of the rules for well-defined use of pointers:

  • The value being loaded, is of the same type that was stored at the address.

  • The provenance of the calculated pointer is valid, being derived from a valid pointer by adding an offset, that gives a pointer still within the original storage instance.

  • There is no mixing of element types within the struct, that would generate padding to make an element offset unpredictable.

But I'm still not sure it's valid to explicitly calculate and use element pointers that way.

CodePudding user response:

C is a low level programming language. This code is well-defined but probably not portable. It is not portable because it makes assumptions about the layout of the struct. In particular, you might run into fields being 64-bit aligned on a 64bit platform where in is 32 bit. Better way of doing it is using the offsetof marco.

CodePudding user response:

The C standard allows there to be arbitrary padding between elements of a struct (but not at the beginning of one). Real-world compilers won’t insert padding into a struct like that one, but the DeathStation 9000 is allowed to. If you want to do that portably, use the offsetof() macro from <stddef.h>.

*(int*)((char*)p   offsetof(foo, c))

is guaranteed to work. A difference, such as offsetof(foo,c) - offsetof(foo, b), is also well-defined. (Although, since offsetof() returns an unsigned value, it’s defined to wrap around to a large unsigned number if the difference underflows.)

In practice, of course, use &p->c.

An expression like the one in your original question is guaranteed to work for array elements, however, so long as you do not overrun your buffer. You can also generate a pointer one past the end of an array and compare that pointer to a pointer within the array, but dereferencing such a pointer is undefined behavior.

  • Related