Почему этот атрибут выравнивания должен быть указан в typedef?
Первоначально я написал этот вопрос со своего планшета и предпринял много быстрых действий, чтобы, по моему мнению, в конечном итоге привести к путанице среди людей, которые читали и / или пытались ответить на вопрос.
Я не прошу решения проблемы, с которой я первоначально начал. Если вы действительно хотите узнать историю, прочитайте следующий параграф, иначе пропустите его.
Это вызвало какой-то старый код, работающий с массивом данных вида {struct, data, struct, data, ...}
где каждый data
имеет произвольную длину. Код обращается к каждой структуре через указатель, и когда мы переключились на gcc, в Solaris произошел сбой из-за неправильного доступа. Одна идея для решения этой проблемы состояла в том, чтобы изменить выравнивание типа, как показано ниже, но я, вероятно, не собираюсь этого делать.
Вопросы, на которые нужно ответить, можно обобщить следующим образом:
- Выравнивание состояний документации не может быть уменьшено с
aligned
, но я могу сделать это с помощью typedef. Это работает как задумано? - Если он работает так, как задумано, зачем ему typedef? Почему я не могу опустить выравнивание как часть определения структуры?
- примечание: это можно сделать с
typedef struct {...}__attribute__((aligned(1))) Typename;
также
- примечание: это можно сделать с
Вот ссылка на пример кода, работающего на wandbox. Если ссылка не работает:
#include <cstdio>
#include <assert.h>
#define ALIGN __attribute__((aligned(1)))
struct Misaligned_1_t { int x; double y; float z; };
struct ALIGN Misaligned_2_t { int x; double y; float z; };
struct Misaligned_3_t { int x; double y; float z; } ALIGN;
// The gcc documentation indicates that the "aligned" attribute
// can only be used to increase alignment, so I was surprised
// to discover this actually works. Why does it work?
typedef Misaligned_1_t ALIGN Aligned_t;
int main( int, char** ) {
char buffer[256];
// The following is meant to simulate a more complicated scenario:
// {SomeStruct, char[arbitrary length], SomeStruct, char[arbitrary length], ...}
// ... where accessing, using and changing each SomeStruct will result in
// misaligned accesses.
auto *m1 = (Misaligned_1_t*)&buffer[1];
auto *m2 = (Misaligned_1_t*)&buffer[1];
auto *m3 = (Misaligned_1_t*)&buffer[1];
auto *a1 = (Aligned_t*)&buffer[1];
// The documentation says we can only reduce alignment with the "packed" attribute,
// but that would change the size/layout of the structs. This is to demonstrate
// that each type is the same size (and should have the same layout).
assert( sizeof(m1) == sizeof(m2)
&& sizeof(m1) == sizeof(m3)
&& sizeof(m1) == sizeof(a1) );
m1->y = 3.14159265358979323846264; // misaligned access
std::printf( "%0.16f\n", m2->y ); // misaligned access
std::printf( "%0.16f\n", m3->y ); // misaligned access
std::printf( "%0.16f\n", a1->y ); // works fine
return 0;
}
2 ответа
Я нашел ответ. Я должен быть слепым. Из документации GCC:
При использовании в структуре или члене структуры выровненный атрибут может только увеличить выравнивание; чтобы уменьшить его, также должен быть указан упакованный атрибут. При использовании в качестве части typedef выровненный атрибут может как увеличивать, так и уменьшать выравнивание, а указание упакованного атрибута генерирует предупреждение.
Из файлов справки gcc
Вы можете указать атрибуты align и transparent_union либо в объявлении typedef, либо сразу после закрывающей фигурной скобки полного определения типа enum, struct или union и упакованного атрибута только после закрывающей фигурной скобки определения.
Так что вы можете использовать
struct Test_t {
int x;
double y;
float z;
} __attribute__((aligned(1)));
а затем определить переменные с
struct Test_t a,b;
struct Test_t *test;
Или вы можете использовать способ, который указан выше. Это то же самое.