4

Let's consider this code:

int i;
int is[10]{};

unsigned char * p = reinterpret_cast<unsigned char*>(&i);
//p defined to point to the object-representation of the first element of array ints
unsigned char * ps = reinterpret_cast<unsigned char*>(&is[0]);

p += sizeof(int);
ps += sizeof(int);
//now ps points to the end of ints[0] and p point to the end of i;

p += sizeof(int); //Undefined behavior according to [expr.add]

ps += sizeof(int); //Undefined behavior?
unsigned char c = *ps;//Undefined behavior?

If we consider that ps points to the object-representation of is[0] then according to pointer arithmetic rule, the behavior is undefined at the two last code line.

Nevertheless, if ps is also a pointer to the object-representation of the array of int is the behavior is defined.

So my question is: does a pointer to the object-representation of a suboject is also a pointer to an element of the object-representation of the containing complete object?

Oliv
  • 17,610
  • 1
  • 29
  • 72
  • I think you're confusing "subobject" with "array member". For member subobjects, comparisons between pointers within the same large object are allowed but arithmetic is not. For pointers to array elements, pointer arithmetic is usable. – Ben Voigt Oct 13 '17 at 21:52
  • @BenVoigt If you follow the link "suboject" you will get: " A subobject can be a member subobject ([class.mem]), a base class subobject ([class.derived]), or an array element". – Oliv Oct 13 '17 at 21:54
  • Are you asking whether or not using `offsetof` and pointer arithmetic with character types is well defined? – StoryTeller - Unslander Monica Oct 13 '17 at 21:56
  • @BenVoigt I do know the rule about pointer arithmetic. Here the question is about pointer arithmetic on object-representation – Oliv Oct 13 '17 at 21:56
  • @RichardCritten I had doubts about that, but apparently, the concept of object representation is tightly coupled to the concept of storage. Here the question I asked, with reasons why I had doubts in the comments, my problem was actualy if the pointer value is valid or not? Apparently it is. https://stackoverflow.com/questions/46735324/how-to-access-an-object-representation-according-to-the-c-standard – Oliv Oct 13 '17 at 22:06
  • I think this is a variation of the old question of whether `((int *)x)[3]` is valid, for `int x[2][2];` – M.M Oct 13 '17 at 22:11
  • https://stackoverflow.com/questions/15217572/pointer-arithmetic-across-subobject-boundaries/ – M.M Oct 13 '17 at 22:15
  • @M.M. I am interested to know if `(int*)x[0])[3]` is valid for `int x[2][2]`. This will help solving this issue. But here the object-representation of the suboject is nested within the object-representation of the comple object. – Oliv Oct 13 '17 at 22:19
  • Similar questions have been discussed dozens of time with no solid conclusions - the standard is not clear on the matter , and compilers do different things – M.M Oct 13 '17 at 22:20
  • @M.M So if compilers do different things, this would mean it is at least concretly undefined behavior! I would be interested to have an exemple. – Oliv Oct 13 '17 at 22:29
  • Not really, it could mean the compiler has a bug – M.M Oct 13 '17 at 22:29
  • There is this CppCon2017 presentation that says it is OK if the subject is an array element and not OK if suboject is a member, [Type Punning in C++](https://github.com/CppCon/CppCon2017/tree/master/Presentations/Type%20Punning%20In%20C%2B%2B17%20-%20Avoiding%20Pun-defined%20Behavior)(slide 84 to 90) but there are no special treatment about object representation of array element and object representation of member suboject in the stantard. – Oliv Oct 13 '17 at 22:34

2 Answers2

3

General case

Yes, the pointer to the object-representation of a suboject is also a pointer to a part of the object-representation of the containing complete object.

This can be deduced from:

1.8/1: An object is a region of storage.

3.9/4: The object representation of an object of type T is the sequence of N unsigned char objects taken up by the object of type T, where N equals sizeof(T).

5.3.3/1: The sizeof operator yields the number of bytes in the object representation of its operand.

1.8/2: Objects can contain other objects, called subobjects. A subobject can be a member subobject, a base class subobject, or an array element. An object that is not a subobject of any other object is called a complete object.

The fact that an object is a region of storage and that the subobject is contained within the region of storage means that the suboject representation is contained in the object representation.

As explained in this other answer, the pointer to the object representation is in fact the pointer to the object.

Specific case of the array

Yes, the pointer to the object representation of a subobject (i.e. in this case an array element) is a pointer to an element of the object-representation of the containing complete object (i.e. in this case the array).

8.4.3/1: (...) An object of array type contains a contiguously allocated non-empty set of N subobjects of type T. (...)

So by definition:

  • the elements of the array are contiguously allocated, meaning that there is no empty space between the elements.

  • the object representation of each element is a sequence of sizeof(T) unsigned chars, and the object representation of the complete array is the resulting N*sizeof(T) unsigned chars.

We can then deduce:

  • As explained in this other answer, the pointer to the object representation is in fact the pointer to the object.

  • the address of the k-th element in the array corresponds to the k-th element in the object representation of the array, and is by construction at an offset of k*sizeof(T) unsigned chars of the address of the array representation.

So the last 2 statements of your code are not undefined behavior, as long as you remain within the boundaries of is

Christophe
  • 68,716
  • 7
  • 72
  • 138
1

There's no such thing as a "pointer to the object representation". You have a pointer which holds the object's address, and that pointer can be derived safely or unsafely.

The "object representation" comes into play when you read within the object's address range using an lvalue of narrow character type, not when the pointer is formed or converted.

Nothing in the rules for reinterpret_cast<unsigned char*>(p) says anything about the result being some special "pointer to object representation". It's still just another alias for the same object.

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
  • Thank you. So you mean that `auto ps = reinterpret_cast (&is[0])` is an alias for the first element of the array `is` so that `ps+2*sizeof(int)` is undefined behavior even if the memory pointed to by `ps+2*sizeof(int)` is inside the `is`'s address range? – Oliv Oct 14 '17 at 07:21
  • Just a comment, I think that [basic.stc.dynamic.safety] does not apply here, because we are not dealing with a pointer allocated by `new` and, whatsoever, all implementations have *relaxed pointer safety*. – Oliv Oct 14 '17 at 07:22
  • @Oliv: No, I didn't say that's undefined behavior. It's the address of an array member, and the pointer arithmetic rules allow adding any offset that lands inside the same array. Which makes your example defined. – Ben Voigt Oct 15 '17 at 01:34