1

The Standard allows us to cast pointers to object type to each other if they are suitably aligned. 6.3.2.3(p7):

A pointer to an object type may be converted to a pointer to a different object type. If the resulting pointer is not correctly aligned68) for the referenced type, the behavior is undefined.

The Standard allows us to copy object representation into char[sizeof(the_object_type)] 6.2.6.1(p4):

The value may be copied into an object of type unsigned char [n] (e.g., by memcpy); the resulting set of bytes is called the object representation of the value.

Also, The Standard explicitly states that

Two values (other than NaNs) with the same object representation compare equal, but values that compare equal may have different object representations.

Consider the following code:

struct contains_64_t{
    uint64_t value;
};

int main(int args, const char *argv[]){
    _Alignas(struct contains_64_t) 
        char buf_2_64t[2 * sizeof(struct contains_64_t)];
    struct contains_64_t c64_1;
    c64_1.value = 1;
    struct contains_64_t c64_2;
    c64_2.value = 2;
    memcpy(buf_2_64t, &c64_1, sizeof(c64_1));
    memcpy(buf_2_64t + sizeof(c64_1), &c64_2, sizeof(c64_2));

    //suitably aligned, ok
    struct contains_64_t *c64_ptr = (struct contains_64_t*) buf_2_64t; 
    printf("Value %"PRIu64"\n", c64_ptr -> value);
}

QUESTION: Is it pedantic to write code like this? If no, what kind of problem we may run into if doing so?

From what I see,

we can cast char* to struct contains_64_t since it is suitably aligned. But the problem is that the declared type of buf is char[2 * sizeof(struct contains_64_t)]. So formally speaking we cannot access the buf through an lvalue of type struct contains_64_t *.

But that would be strange since we have suitably aligned pointer and literally identical object representation. Surely we could declare struct contains_64_t buf[2];, but the solution would not work in case of struct containing variably length array

UPD: Would doing such buffer alignment be enough if we assume we are compiling with GCC?

Some Name
  • 8,555
  • 5
  • 27
  • 77
  • Alignment is not the potential UB here, by anti-aliasing is a concern perhaps with `struct contains_64_t *c64_ptr = (struct contains_64_t*) buf_2_64t;`. `printf("Value = %lu\n", c64_ptr -> value);` is certainly UB with 32-bit `long`. – chux - Reinstate Monica Mar 20 '19 at 03:29
  • @chux Why is casting to a suitably aligned pointer UB? Or you mean UB is caused by incorrect use of `printf`? Fixed. – Some Name Mar 20 '19 at 03:32
  • Some 1) _casting_ is not the UB concern, [anti-aliasing](https://stackoverflow.com/questions/98650/what-is-the-strict-aliasing-rule) UB may apply here. 2) `%lu` is for `unsigned long`, not necessarily `uint64_t`. – chux - Reinstate Monica Mar 20 '19 at 03:36
  • @chux Agree with `2)`. Replaced it with `PRIu64` defined in `inttypes.h`. – Some Name Mar 20 '19 at 03:37
  • @chux Anyway if we do not align the pointer to stack-allocated buffer we cannot convert it. So UB will also come at `struct contains_64_t *c64_ptr = (struct contains_64_t*) buf_2_64t;` otherwise the Standard permits such convertation at `6.2.3.2(p7)`. – Some Name Mar 20 '19 at 03:46

1 Answers1

2

memcpy()'s look OK.

c64_ptr -> value is UB.

An object shall have its stored value accessed only by an lvalue expression that has one of the following types:

— a type compatible with the effective type of the object,

— a qualified version of a type compatible with the effective type of the object,

— a type that is the signed or unsigned type corresponding to the effective type of the object,

— a type that is the signed or unsigned type corresponding to a qualified version of the effective type of the object,

— an aggregate or union type that includes one of the aforementioned types among its members (including, recursively, a member of a subaggregate or contained union), or

— a character type.

Look up compatible in the standard to complete the picture.

Alexey Frunze
  • 61,140
  • 12
  • 83
  • 180