6

Let's consider following piece of code:

struct Blob {
    double x, y, z;
} blob;

char* s = reinterpret_cast<char*>(&blob);
s[2] = 'A';

Assuming that sizeof(double) is 8, does this code trigger undefined behaviour?

TemplateRex
  • 69,038
  • 19
  • 164
  • 304
SergeyA
  • 61,605
  • 5
  • 78
  • 137
  • 4
    This wouldn't even compile without a cast though. – dgross Jan 19 '16 at 14:55
  • Is this C? In C++ this will not compile. – Bathsheba Jan 19 '16 at 14:55
  • 1
    This is not legal C or C++, you need a cast. Once you have a cast, this is legal and well-defined. Of course anything can happen if you try to access blob.x afterwards. – n. m. could be an AI Jan 19 '16 at 14:56
  • @Bathsheba, added the cast, question remains. – SergeyA Jan 19 '16 at 14:57
  • @n.m. hint - the question is regarding pointer arithmetics on non-arrays. – SergeyA Jan 19 '16 at 14:57
  • Pointer arithmetic is done on pointers. All pointers involved must point into the same array. Any POD/trivial-layout/whatever-its-name object can be treated as an array of `char` or its differently signed siblings. This is the basis of functions like `memcpy` or `fread`. – n. m. could be an AI Jan 19 '16 at 15:02
  • @hvd You do realize that the quote refers to the fact that both pointers point to the same array (or to the hypothetical element past it)? Your interpretation is incorrect. – Columbo Jan 19 '16 at 15:02
  • @n.m., this is common sense answer, not a language-lawyer one :) – SergeyA Jan 19 '16 at 15:03
  • In this particular case I bet on **no undefined behavior here**. You struct has only `double`s in it so no padding is necessary. In case if padding __is__ required you could get a __unspecified__ behavior (I guess). Still a language-lawyer opinion required. – DennisS Jan 19 '16 at 15:06
  • I don't see any pointer arithmetic happening here. I see array accessing, but that's not pointer arithmetic. – Nicol Bolas Jan 19 '16 at 15:06
  • 6
    @NicolBolas s[2] = *(s+2) – TemplateRex Jan 19 '16 at 15:07

2 Answers2

5

Quoting from N4140 (roughly C++14):

3.9 Types [basic.types]

2 For any object (other than a base-class subobject) of trivially copyable type T, whether or not the object holds a valid value of type T, the underlying bytes (1.7) making up the object can be copied into an array of char or unsigned char.42 If the content of the array of char or unsigned char is copied back into the object, the object shall subsequently hold its original value.

42) By using, for example, the library functions (17.6.1.2) std::memcpy or std::memmove.

3 For any trivially copyable type T, if two pointers to T point to distinct T objects obj1 and obj2, where neither obj1 nor obj2 is a base-class subobject, if the underlying bytes (1.7) making up obj1 are copied into obj2,43 obj2 shall subsequently hold the same value as obj1. [ Example: ... ]

43) By using, for example, the library functions (17.6.1.2) std::memcpy or std::memmove.

This does, in principle, allow assignment directly to s[2] if you take the position that assignment to s[2] is indirectly required to be equivalent to copying all of some other Blob into an array that just happens to be bytewise identical except for the third byte, and copying it into your Blob: you're not assigning to s[0], s[1], etc. For trivially copyable types including char, that is equivalent to setting them to the exact value they already have, which also has no observable effect.

However, if the only way to get s[2] == 'A' is by memory manipulation, then a valid argument could also be made that what you're copying back into your Blob isn't the underlying bytes that made up any previous Blob. In that case, technically, the behaviour would be undefined by omission.

I do strongly suspect, especially given the "whether or not the object holds a valid value of type T" comment, that it's intended to be allowed.

  • Comments are not for extended discussion; this conversation has been [moved to chat](http://chat.stackoverflow.com/rooms/101136/discussion-on-answer-by-hvd-pointer-arithmetics-on-non-array-types). – George Stocker Jan 19 '16 at 23:15
0

Chapter 3.10 of the standard seems to allow for that specific case, assuming that "access the stored value" means "read or write", which is unclear.

3.10-10

If a program attempts to access the stored value of an object through a glvalue of other than one of the following types the behavior is undefined:

—(10.1) the dynamic type of the object,

—(10.2) a cv-qualified version of the dynamic type of the object,

—(10.3) a type similar (as defined in 4.4) to the dynamic type of the object,

—(10.4) a type that is the signed or unsigned type corresponding to the dynamic type of the object,

—(10.5) a type that is the signed or unsigned type corresponding to a cv-qualified version of the dynamic type of the object,

—(10.6) an aggregate or union type that includes one of the aforementioned types among its elements or nonstatic data members (including, recursively, an element or non-static data member of a subaggregate or contained union),

—(10.7) a type that is a (possibly cv-qualified) base class type of the dynamic type of the object,

—(10.8) a char or unsigned char type.

Community
  • 1
  • 1
Ilya
  • 5,377
  • 2
  • 18
  • 33