Изменения выравнивания типов и последствия

При переносе программного обеспечения одной из забот, которая ложится на плечи разработчика является изменение размерности типов и правил их выравнивания. Не так давно мы поддержали в анализаторе Viva64 диагностическое правило, позволяющее обнаружить структуры данных, неэффективно использующие память на 64-битных системах. Но в данном направлении еще стоит продолжать исследования и я внимательно просматриваю сообщения в форумах по этому поводу.

В этот раз мое внимание привлекло сообщение в форуме RSDN [1] следующего содержания:

Столкнулся сегодня с одной проблемой в Linux. Есть структура данных, состоящая из нескольких полей: 64-битный double, потом 8 unsigned char и один 32-битный int. Итого получается 20 байт (8 + 8*1 + 4). Под 32-битными системами sizeof равен 20 и всё работает нормально. А под 64-битным Linux'ом sizeof возвращает 24. Т.е. идёт выравнивание по границе 64 бит.

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

Когда меняются размеры полей в структуре и из-за этого меняется сам размер структуры это понятно и привычно. Но здесь другой случай. Размер полей остался прежний, но из-за иных правил выравнивания размер структуры все равно изменится. Такое поведение может привести к разнообразным ошибкам, например в несовместимости форматов сохраняемых данных.

Viva64 пока не поддерживает Linux системы, и я решил выяснить может ли возникнуть данный тип ошибок и в Windows системах. Для этого я взял из статьи "C++ data alignment and portability" [2] пример кода, выводящий на печать размер типов и их выравнивание. Немного модифицировал его для Visual Studio, после чего получилась вот такая программа:

#include <iostream>
using namespace std;
template <typename T>
void print (char const* name)
{
  cerr << name
       << " sizeof = " << sizeof (T)
       << " alignof = " << __alignof (T)
       << endl;
}
int _tmain(int, _TCHAR *[])
{
  print<bool>        ("bool          ");
  print<wchar_t>     ("wchar_t       ");
  print<short>       ("short int     ");
  print<int>         ("int           ");
  print<long>        ("long int      ");
  print<long long>   ("long long int ");
  print<float>       ("float         ");
  print<double>      ("double        ");
  print<long double> ("long double   ");
  print<void*>       ("void*         ");
}

Полученные данные, я совместил с данными из статьи "C++ data alignment and portability" для GNU/Linux систем и привожу их в таблице N1.

Таблица N1.Размеры типов и их выравнивание.

Таблица N1.Размеры типов и их выравнивание.

Давайте изучим эту таблицу. Обратите внимание на выделенные ячейки, относящиеся к типам long long int и double. Эти типы не меняют свои размеры в зависимости от разрядности архитектуры. На 32-битной и на 64-битных системах они имеют размер 8 байт. Но выравнивание для 32-битных и 64-битных систем различно. Это как раз и может привести к изменению размера структуры. Когда мы будем реализовывать Viva64 под Linux. мы обязательно учтем возможность возникновения связанных с этим потенциальных ошибок.

В Windows системах подобных потенциальных проблем с изменением выравнивания не наблюдается. Обратите внимание, что выравнивание всех типов остается неизменным или меняется вместе с изменением размера типа. Хорошо. У Windows разработчиков одной потенциальной проблемой меньше.

Библиографический список