Объединение по умолчанию и выравнивание структуры в C?

Каково выравнивание по умолчанию глобальных переменных, которые являются объединениями или структурами. Гарантировано ли, что они будут выровнены по словам? В частности, при использовании GCC и SDCC.

В коде функция f() безопасна или она может вызвать невыровненный доступ? Есть ли разница между 16-битной и 32-битной платформами?

#define ADDR_SIZE 8

typedef union {
   unsigned char u8[ADDR_SIZE];
} addr_t;

addr_t global_address;

void f(void) {
   uint32_t x = *((uint32_t *)&global_address) + *((uint32_t *)&global_address + 1);
}

person kfx    schedule 08.10.2015    source источник
comment
Используйте прагмы для обеспечения упаковки структур. См. ЗДЕСЬ   -  person LPs    schedule 08.10.2015
comment
Термин Word не имеет определенного размера/ширины, поэтому здесь он бесполезен. Стандарт не навязывает (не может) какого-либо согласования.   -  person too honest for this site    schedule 08.10.2015
comment
Прагмы не являются опцией, поскольку код должен быть независимым от компилятора в соответствии с правилами проекта.   -  person kfx    schedule 08.10.2015
comment
@kfx: Таким образом, вы не можете полагаться на какое-либо выравнивание или дополнение структуры и т. Д. Звучит как проблема XY. Укажите, чего вы хотите достичь. Обратите внимание, что приведения вызывают неопределенное поведение и строгие нарушения псевдонимов. Общее правило — не использовать их, если они вам действительно не нужны, и вы точно знаете, что они делают, включая все последствия!   -  person too honest for this site    schedule 08.10.2015
comment
Моя цель — просмотреть этот код и определить, безопасен ли он.   -  person kfx    schedule 08.10.2015
comment
Вы не можете быть уверены в размере слова. Вы можете указать размер указателя, чтобы узнать, является ли архитектура 8/16/32/64-битной, но некоторые процессоры технически, например, 64-битные, но имеют режимы для поддержки устаревшего кода. Процессоры x86 (процессоры Intel/AMD) могут быть 64-разрядными, но запускать код в 16- или 32-разрядном режиме [и даже не включать 64-разрядные возможности, например, в случае стандартной установки Windows XP.   -  person LPs    schedule 08.10.2015
comment
В вашем случае ваш код всегда безопасен, потому что uint32_t всегда имеет 32-битную переменную, а ваш союз состоит из одного члена.   -  person LPs    schedule 08.10.2015
comment
@LPs В коде нет переменных uint32_t. Глобальная переменная, представляющая собой объединение, приводится к типу uint32_t.   -  person kfx    schedule 08.10.2015
comment
global_address превращается в uint32_t *   -  person LPs    schedule 08.10.2015
comment
Почему бы на самом деле не сделать объединение членом uint32_t, чтобы вы не выполняли приведение явно, и чтобы компилятор со 100% уверенностью знал, что объединение должно быть выровнено для поддержки uint32_t? union с одним членом довольно глупо.   -  person ShadowRanger    schedule 08.10.2015
comment
В зависимости от конфигурации #defines могут быть и другие члены, это просто не имеет отношения к вопросу.   -  person kfx    schedule 08.10.2015
comment
Тогда вы ДОЛЖНЫ опубликовать свой реальный код....   -  person LPs    schedule 08.10.2015
comment
Это настоящий код. Я ценю конструктивные комментарии, но давайте не будем засорять дискуссию не очень важными вещами, если вы не можете дать ответ с хорошей ссылкой.   -  person kfx    schedule 08.10.2015
comment
@kfx, я не думаю, что в комментариях есть что-то неуместное.   -  person Jens Gustedt    schedule 08.10.2015
comment
@JensGustedt Это: global_address приводится к uint32_t *, в частности, является ложным, поэтому неактуальным и дезинформативным ... К этому приводится address переменной.   -  person kfx    schedule 08.10.2015
comment
@kfx, ну, наверное, это то, что они имели в виду, и это просто ошибочно, так что это все-таки было актуально. А потом вы попросили людей о помощи, не так ли?   -  person Jens Gustedt    schedule 08.10.2015


Ответы (2)


Если вы не указали требования к выравниванию, используя, например, __attribute__((aligned(4))), вы не можете гарантировать правильное выравнивание объединения.

При тщательном расположении глобальных переменных с использованием char, как здесь:

...
#define ADDR_SIZE 8

typedef union {
   unsigned char u8[ADDR_SIZE];
} addr_t;

addr_t global_address1;
char padd1;
addr_t global_address2;
addr_t global_address3;
...

Вы можете увидеть нечетный адрес:

0x804971d * &global_address1
0x8049714
0x8049715 * &global_address2
0x804970c

Попытка получить доступ к этим адресам в некоторых архитектурах со строгими требованиями к выравниванию вызовет исключение неправильного доступа и остановит программу. Другие архитектуры, которые могут обрабатывать несогласованный доступ за счет снижения производительности, потребуют по крайней мере двух циклов чтения памяти, а для завершения потребуется несколько циклов ЦП.

person Community    schedule 08.10.2015
comment
Если вы не указали требования к выравниванию... вы не можете гарантировать правильное выравнивание объединения. --. Есть и другие способы, которые гарантируют. - person chux - Reinstate Monica; 07.02.2021
comment
Представленный код не гарантирует, что global_address2 будет меньше выравнивания по слову. - person John Bollinger; 07.02.2021

Каково выравнивание по умолчанию глобальных переменных, которые являются объединениями или структурами.

Это зависит от членов профсоюза.

Гарантировано ли, что они будут выровнены по словам?

Нет. (Предположим, что слово 4-байтовое). Требования к выравниванию сложны. Хотя они редко превышают sizeof(int), они могут различаться для каждого типа.


В C11 при включении объекта max_align_t объединение будет соответствующим образом выровнено по любому типу.

max_align_t, который является типом объекта, выравнивание которого настолько велико, насколько это поддерживается реализацией во всех контекстах; С11 §7.19 2

#include <stddef.h>

typedef union {
   max_align_t dummy;
   unsigned char u8[ADDR_SIZE];
} addr_t;

У @Jens Gustedt есть хорошие замечания по поводу псевдонимов. Просто получите доступ к uint32_t из союза. Остерегайтесь проблем с порядком байтов.

typedef union {
   unsigned char u8[ADDR_SIZE];
   uint32_t u32[ADDR_SIZE/sizeof(uint32_t)];
} addr_t;
person chux - Reinstate Monica    schedule 08.10.2015