Это расположение памяти и информирование компилятора. Компилятор может выбрать размещение структуры по выровненному адресу, но это не обязательно будет делать, если структура заменяется всеми содержащимися в ней элементами. Может быть разница в байтах заполнения.

я есть структура X, которая наследует от структуры Base. Тем не менее, в моей текущей настройке, из-за выравнивания, размер X равен 24B:

typedef struct {
    double_t a;
    int8_t b;
} Base;

typedef struct {
    Base base;
    int8_t c;
} X;

Чтобы сохранить память, я хотел бы развернуть структуру Base, поэтому я создал структуру Y, которая содержит поля из Base (в том же порядке, всегда в начале структуры), поэтому размер структуры составляет 16B :

typedef struct {
    double_t base_a;
    int8_t base_b;
    int8_t c;
} Y;

Затем я собираюсь использовать экземпляр структуры Y в методе, который ожидает указатель на структуру Base:

void print_base(Base* b)
{
  printf("%f %d\n", b->a, b->b);
}
// ...
Y data;
print_base((Base*)&data);

Нарушает ли приведенный выше код строгое правило псевдонимов и вызывает неопределенное поведение?

 Marcin Kolny08 дек. 2017 г., 09:59
@ МЧ, это не так. Размер структуры X, которая использует ваш подход, занимает 24 байта, в то время как структура Y только 16
 Marcin Kolny08 дек. 2017 г., 10:05
@Marian Я хотел бы представить правильный подход и объяснить причину, почему я не хочу идти на это.
 Marian08 дек. 2017 г., 10:04
Мне кажется, что весь тип X не имеет значения в этом примере. Вы кастуете из Y в базу, которая не зависит от X.
 mch08 дек. 2017 г., 09:57
Почему бы вам просто не заявитьBase base; вY и вызвать функцию сprint_base(&data.base);? Он использует ту же память, что и члены структуры в другой структуре.
 Marcin Kolny08 дек. 2017 г., 09:56
Правильно, исправил пример кода

Ответы на вопрос(1)

Решение Вопроса

Base а такжеY несовместимые типы, как определено стандартом 6.2.7, все члены должны совпадать.

Чтобы получить доступ кY черезBase* без создания строгого нарушения псевдонимов,Y должен быть «агрегатный тип» (он есть), который содержитBase введите среди его членов. Это не.

Так что это строгое нарушение алиасинга и, кроме того, посколькуY а такжеBase несовместимы, они могут иметь разные макеты памяти. В этом вся суть, вы сделали их разными по этой причине :)

В подобных ситуациях вы можете использовать союзы с членами структуры, которые разделяютобщая начальная последовательность, что является особым разрешенным случаем. Пример правильного кода из C11 6.5.2.3:

union {
  struct {
    int alltypes;
  } n;
  struct {
    int type;
    int intnode;
  } ni;
  struct {
    int type;
    double doublenode;
  } nf;
} u;

u.nf.type = 1;
u.nf.doublenode = 3.14;
/* ... */
if (u.n.alltypes == 1)
  if (sin(u.nf.doublenode) == 0.0)
 Lundin08 дек. 2017 г., 14:03
Это расположение памяти и информирование компилятора. Компилятор может выбрать размещение структуры по выровненному адресу, но это не обязательно будет делать, если структура заменяется всеми содержащимися в ней элементами. Может быть разница в байтах заполнения.
 Marian08 дек. 2017 г., 12:00
@MarcinKolny Например:blog.regehr.org/archives/1307 , Чтобы получить больше, поместите "строгий псевдоним" в Google.
 Marcin Kolny08 дек. 2017 г., 11:26
@Marian спасибо, звучит интересно. Просто любопытно, не могли бы вы дать мне несколько статей, где я мог бы прочитать больше об этом?
 Marcin Kolny08 дек. 2017 г., 10:14
Благодарю. Я не уверен насчет расположения памяти. Разве стандарт не гарантирует, что смещение для двух первых полей (a, b в Base, base_a, base_b в Y) будет одинаковым?
 Marian08 дек. 2017 г., 10:55
@MarcinKolny Речь идет не о макете памяти. Расположение памяти одинаково в обоих типах. Это про информирование оптимизатора компилятора. Вы должны определитьunion {Base b; Y y;}; в своем заголовке, чтобы сообщить оптимизатору, что он должен перезагрузить значениеb.a из памяти в любое время, когда значениеy.base_a изменилось.

Ваш ответ на вопрос