Позиция памяти элементов в объединении C/С++

У меня есть объединение в C, как это:

union AUnion {
  struct CharBuf {
    char *buf;
    size_t len;
  } charbuf;
  uint8_t num;
  double fp_num;
};

Мой вопрос в том, могу ли я гарантировать, что если дано следующее:

union AUnion u;

Тогда верно следующее:

&u == &u.num
&u == &u.fp_num
&u == &u.charbuf

I. все они начинаются в начале сегмента памяти, где хранится u.

В случае этой C-программы, скомпилированной с gcc version 5.3.0 и -std=c11, это верно:

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>

union AUnion {
    struct CharBuf {
        char *buf;
        size_t len;
    } charbuf;
    uint8_t num;
    double fp_num;
};

int main(void)
{
    union AUnion u;
    printf("%d\n", ((void*)&u) == ((void*)&u.charbuf));
    printf("%d\n", ((void*)&u.charbuf) == ((void*)&u.num));
    printf("%d\n", ((void*)&u.num) == ((void*)&u.fp_num));
}

Как он печатает:

1
1
1

Компиляция кода выше как С++ 11 с тем же результатом компилятора в том же выпуске, что и компиляция C11.

Но это стандартизированное поведение? Это undefined? Могу ли я полагаться на это поведение с большинством компиляторов C? Могу ли я ожидать такого поведения с компиляторами С++?

Ответы

Ответ 1

В 6.7.2.1p16 стандарт C гарантирует, что:

Размер объединения достаточен, чтобы содержать самый большой из его членов. Значение не более одного из членов может быть сохранено в объединенном объекте в любое время. Указатель на объект объединения, соответствующим образом преобразованный, указывает на каждый из его членов (или если элемент является битовым полем, а затем блоку, в котором он находится) и наоборот.

Итак, да, вы можете положиться на всех участников, начиная с адреса union (обратите внимание, что это одно и то же для первого члена struct).

В стандарте С++ есть аналогичное предложение относительно C-стиля (т.е. только члены стиля C) union s/struct s, потому что С++ позволяет передавать union в C-функции, которые требуют этого макета. Соответствующий раздел в стандарте С++ - 9.5.


Однако, обратите внимание, что в стандартных простых типах (целые числа, поплавки) могут быть биты заполнения. И их внутренние могут отличаться (endianess). Вы также можете нарушить правило строгого сглаживания (C: эффективный тип).

Ответ 2

Из моего опыта я бы сказал "да", хотя я проверил С++ 14 стандарт, и это даже гарантирует это. (С++ 11, скорее всего, будут иметь одинаковые эффекты). Глава 9.5: All non-static data members of a union object have the same address

Итак, вы можете зависеть от этого поведения.