Почему выравнивание длинного и длинного члена объединения больше, чем содержащее объединение / структура? Это правильно?

Из этого вопроса можно было начать верить, что выравнивание союза не меньше, чем наибольший расклад его отдельных членов. Но у меня проблема с типом long long в gcc / g ++. Полный пример можно найти здесь, но вот части, относящиеся к моему вопросу:

union ull {
  long long m;
};

struct sll {
  long long m;
};


int main() {
#define pr(v) cout << #v ": " << (v) << endl
   pr(sizeof(long long));
   pr(__alignof__(long long));
   pr(sizeof(ull));
   pr(__alignof__(ull));
   pr(sizeof(sll));
   pr(__alignof__(sll));
};

Это приводит к следующему выводу:

sizeof(long long): 8
__alignof__(long long): 8
sizeof(ull): 8
__alignof__(ull): 4
sizeof(sll): 8
__alignof__(sll): 4

Почему выравнивание члена союза больше, чем выравнивание содержащего союза?

[ОБНОВЛЕНИЕ]

Согласно ответу Кита, alignof здесь неверен. Но я проверяю следующее, и кажется, что alignof говорит нам правду. Видеть:

union ull {
  long long m;
};
long long a;
char b;
long long c;
char d;
ull e;
int main() {
#define pr(v) cout << #v ": " << (v) << endl
   pr(size_t((void*)&b));
   pr(size_t((void*)&c));
   pr(size_t((void*)&d));
   pr(size_t((void*)&e));
   pr(size_t((void*)&c) - size_t((void*)&b));
   pr(size_t((void*)&e) - size_t((void*)&d));
};

Выход:

size_t((void*)&b): 134523840
size_t((void*)&c): 134523848
size_t((void*)&d): 134523856
size_t((void*)&e): 134523860
size_t((void*)&c) - size_t((void*)&b): 8
size_t((void*)&e) - size_t((void*)&d): 4

Итак, выравнивание long long равно 8, а выравнивание объединения, содержащего long long, равно 4 в глобальных данных. Для локальной области я не могу проверить это, поскольку кажется, что компилятор может переупорядочивать локальные данные, поэтому этот трюк не работает. Вы можете это прокомментировать?

[/ ОБНОВЛЕНИЕ]


person PiotrNycz    schedule 06.08.2012    source источник
comment
Я вижу то же самое в Red Hat, gcc 4.7.0 с -m32, но нет с -m64 (все 8s).   -  person BoBTFish    schedule 06.08.2012
comment
gcc.gnu.org/bugzilla/show_bug.cgi?id=52023 ссылка на соответствующую ошибку gcc для _Alignof (C11).   -  person Marc Glisse    schedule 19.01.2014


Ответы (1)


__alignof__ (который является расширением gcc) не обязательно сообщает о необходимом выравнивании для типа.

Процессоры x86, например, на самом деле не требуют более чем 1-байтового выравнивания для любого типа. Доступ к 4-байтовому или 8-байтовому объекту, вероятно, будет более эффективным, если объект выровнен по словам, но выравнивания байтов достаточно.

Цитата из документации gcc:

Некоторые машины фактически никогда не требуют юстировки; они позволяют ссылаться на любой тип данных даже по нечетному адресу. Для этих машин __alignof__ сообщает о наименьшем выравнивании, которое GCC предоставит типу данных, обычно в соответствии с требованиями целевого ABI.

Но это все еще не дает ответа на вопрос. Даже с таким расплывчатым определением я не могу придумать какой-либо веской причины для __alignof__ указывать более строгое выравнивание для long long, чем для структуры или объединения, содержащих long long.

Более переносимый метод определения выравнивания типа следующий:

#define ALIGNOF(type) ((int)(offsetof(struct {char c; type t;}, t)))

Это дает смещение члена типа t в структуре, состоящей из char и t. Используя этот макрос, эта программа:

#include <iostream>
#include <cstddef>

union ull {
  long long m;
};

struct sll {
  long long m;
};

#define ALIGNOF(type) ((int)(offsetof(struct {char c; type t;}, t)))

int main() {
#define pr(v) std::cout << #v ": " << (v) << std::endl
   pr(sizeof(long long));
   pr(__alignof__(long long));
   pr(ALIGNOF(long long));
   pr(sizeof(ull));
   pr(__alignof__(ull));
   pr(ALIGNOF(ull));
   pr(sizeof(sll));
   pr(__alignof__(sll));
   pr(ALIGNOF(sll));
};

производит этот вывод в моей системе (gcc-4.7, Ubuntu 12.04, x86):

sizeof(long long): 8
__alignof__(long long): 8
ALIGNOF(long long): 4
sizeof(ull): 8
__alignof__(ull): 4
ALIGNOF(ull): 4
sizeof(sll): 8
__alignof__(sll): 4
ALIGNOF(sll): 4

Результаты, указанные моим макросом ALIGNOF(), согласованы: long long имеет 4-байтовое выравнивание, а структура или объединение, содержащие long long, имеют 4-байтовое выравнивание.

Я подозреваю, что это ошибка или, по крайней мере, несоответствие в реализации __alignof__ в gcc. Но из-за нечеткости определения трудно быть уверенным, что это действительно ошибка. Похоже, об этом не сообщалось.

Обновление:

Возможно, я прыгаю, но я только что отправил отчет об ошибке.

Этот предыдущий отчет об ошибке, закрытый как «INVALID», аналогичен, но это не относится к выравниванию самой конструкции.

Обновление 2:

Мой отчет об ошибке был закрыт как дубликат предыдущего. Я прошу разъяснений.

person Keith Thompson    schedule 06.08.2012
comment
Большое спасибо за эту ALIGNOF() подсказку. - person PiotrNycz; 06.08.2012
comment
Однако этот макрос не работает для типов NonPOD, поскольку offsetof не работает с NonPOD (см. stackoverflow.com/questions/1129894/). Вы случайно не знаете решение для типов NonPOD? - person PiotrNycz; 06.08.2012
comment
Вы можете прокомментировать мое обновление. Кажется, что __alignof__(long long)==8 правильно. И то, что проверяется с вашим ALIGNOF, - это просто выравнивание long long в struct, что является тем же случаем, что и мой struct sll { long long m; }. Возможно, это поможет в вашем отчете об ошибке. - person PiotrNycz; 06.08.2012
comment
@PiotrNycz: Это сложно; объект, выровненный по 8 байтов, также выровнен по 4 байтам, поэтому показ того, что объект long long имеет выровненный по 8 байтов адрес, не демонстрирует, что его выравнивание равно 8. Но дальнейший эксперимент показывает, что gcc делает похоже, выравнивают long long объекты по 8-байтовым границам и struct { long long x; } объекты по 4-байтовым границам. - person Keith Thompson; 06.08.2012