Home > front end >  Updating part of a wider integer through a uint8_t reference
Updating part of a wider integer through a uint8_t reference

Time:12-23

#include <cstdio>
#include <cstdint>


struct Registers {
    Registers() : af(0),
    f(*(reinterpret_cast<uint8_t *>(&af))),
    a(*(reinterpret_cast<uint8_t *>(&af)   1)) {

    }
    std::uint16_t af;
    std::uint8_t& f;
    std::uint8_t& a;  
};

int main() {
    Registers r;
    r.af = 0x00FF;
    r.a = 0xAA;
    std::printf("AF: X A: X F: X\n", r.af, r.a, r.f);
    return 0;
}

Regardless of endianness issues, is this legal c , or does it invoke some type of undefined behavior? I think this should be fine with pointers and does not violate strict aliasing, since uint8_t is a char type, but I am not sure if this is legal through references.

This seems to work fine with most compiler flags turned on and does not throw any warnings:

$ clang   reg.cpp -O3 -fsanitize=undefined -fstrict-aliasing -Wall && ./a.out
AF: AAFF A: AA F: FF

CodePudding user response:

As you have noted in your question, the fact that you are casting to a "byte" type almost certainly removes any issues of violating strict aliasing requirements.

Nevertheless, from a strict, "language-lawyer" perspective, the reinterpret_cast<uint8_t *>(&af) 1 expression potentially invokes undefined behaviour – because the pointer operand is not the address of an array element, and array elements are the only types on which such pointer arithmetic is well-defined by the Standard.

From this Draft C 17 Standard (bold emphasis mine):

8.5.6 Additive operators       [expr.add]



4     When an expression that has integral type is added to or subtracted from a pointer, the result has the type of the pointer operand. If the expression P points to element x[i] of an array object x with n elements the expressions P J and J P (where J has the value j) point to the (possibly-hypothetical) element x[i j] if 0 ≤ i jn; otherwise, the behavior is undefined. Likewise, the expression P - J points to the (possibly-hypothetical) element x[i − j] if 0 ≤ i − jn; otherwise, the behavior is undefined.

However, whether or not it is legal and well-defined to consider a uint16_t variable as an array of (two) uint8_t elements is where there may be room for debate.


Notes/Discussion

After some very helpful comments, I am now (even more) convinced that, even though there may formally be undefined behaviour in the pointer addition expression, there is no situation (at least, on any sane platform) where the code presented in the original question will not work as intended.

First, as pointed out by Chris Dodd, §6.7 (paragraph 2) of the above-quoted Draft Standard has this:

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 (6.6.1) making up the object can be copied into an array of char, unsigned char, or std::byte. If the content of that array is copied back into the object, the object shall subsequently hold its original value.

This confirms that the uint16_t data can be treated – at least, in terms of its memory layout – as a 2-element array of uint8_t data.

Second, as pointed out by Language Lawyer, an object of non-array type can be considered an array of a single element; further, pointer arithmetic on the address of such an object is allowed to result in the address of the "one-pass-the-end" hypothetical element. From a slightly later Draft Standard (§6.8.3) [basic.compund]:

3.4     For purposes of pointer arithmetic ([expr.add]) and comparison ([expr.rel], [expr.eq]), a pointer past the end of the last element of an array x of n elements is considered to be equivalent to a pointer to a hypothetical array element n of x and an object of type T that is not an array element is considered to belong to an array with one element of type T.

Thus, combining the above two, the hypothetical "one-pass-the-end" uint8_t element referenced by the result of the pointer addition will be the second byte of the uint16_t data object.

  • Related