The rule you are looking for is 3.9p4:
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)
. The value representation of an object is the set of bits that
hold the value of type T
. For trivially copyable types, the value representation is a set of bits in the object representation that determines a value, which is one discrete element of an implementation-defined set of values.
So if you use unsigned char
, you do get implementation-defined behavior (any conforming implementation must give you a guarantee on what that behavior is).
Reading through char
is also legal, but then the values are unspecified. You are however guaranteed that using unqualified char
will preserve the value (therefore bare char
cannot have trap representations or padding bits), according to 3.9p2:
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
. 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.
("unspecified" values are a bit weaker than "implementation-defined" values -- the semantics are the same but the platform is not required to document what the values are.)