Does the char * pointer add a UB when it does not actually point to a char array?

C ++ 17 ( expr.add / 4 ):

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

struct Foo {
    float x, y, z;
};

Foo f;
char *p = reinterpret_cast<char*>(&f) + offsetof(Foo, z); // (*)
*reinterpret_cast<float*>(p) = 42.0f;

Does the line marked with (*) UB? reinterpret_cast<char*>(&f)does not point to a char array, but to a float, so it should UB in accordance with the paragraph given. But if it is UB, then offsetofutility will be limited.

Is it UB? If not, why not?

+10
source share
5 answers

The addition should be valid, but I do not believe that the standard manages to say this quite clearly. Quoting N4140 (approximately C ++ 14):

3.9 Types [basic.types]

2 ( ) - T, , T, (1.7), , char unsigned char. 42 [...]

42) , , (17.6.1.2) std::memcpy std::memmove.

"", std::memcpy std::memmove , . , for, .

, , , , , .

, +, - , ( , ) , , .

+6

, offsetof :

#include <assert.h>
#include <stddef.h>
struct S { float a, b, c; };

const size_t idx_S[] = {
    offsetof(struct S, a),
    offsetof(struct S, b),
    offsetof(struct S, c),
};

float read_S(struct S *sp, unsigned int idx)
{
    assert(idx < 3);
    return *(float *)(((char *)sp) + idx_S[idx]); // intended to be valid
}

, , :

#include <assert.h>
#include <stddef.h>
struct S { float a[2]; float b[2]; };

static_assert(offsetof(struct S, b) == sizeof(float)*2,
    "padding between S.a and S.b -- should be impossible");

float read_S(struct S *sp, unsigned int idx)
{
    assert(idx < 4);
    return sp->a[idx]; // undefined behavior if idx >= 2,
                       // reading past end of array
}

, C C++, , , .

" ?" . , C C++, 1990- , , , , ( "" , "" ).

(. , C, , - C++.)

+3

, . char § 3.10 ¶ 10.8:

gl , , undefined:

  • [...]
  • a char unsigned char.

: char* float* . Foo POD-, . POD (, UB), . , , private -POD-. , UB, , , int* , float. .

+1

, . ,

reinterpret_cast<char*>(&f) , float ,...

... reinterpret_cast<char*>(&f) , , , - .

offsetof

struct Foo {
    float x, y, z;
};

Foo f;
auto p = reinterpret_cast<std::uintptr_t>(&f) + offsetof(Foo, z); 
                       // ^^^^^^^^^^^^^^
*reinterpret_cast<float*>(p) = 42.0f;
+1

. CWG 1314

6.9 [basic.types] 4,

T - N char, T, N sizeof (T).

4.5 [intro.object] 5,

(6.9 [basic.types]) .

(8.7 [expr.add] 5) (, memcpy?

( 2011 .):

, .

CWG , " ", , , .

CWG , unsigned char , , unsigned char, . , , char ( ++ 17) std::byte. (, , , , .)

( , std::launder , . ; , .)

0

All Articles