Почему этот атрибут выравнивания должен быть указан в 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;

Или вы можете использовать способ, который указан выше. Это то же самое.

Другие вопросы по тегам