При переносе программного обеспечения одной из забот, которая ложится на плечи разработчика является изменение размерности типов и правил их выравнивания. Не так давно мы поддержали в анализаторе 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. Размеры типов и их выравнивание.
Давайте изучим эту таблицу. Обратите внимание на выделенные ячейки, относящиеся к типам long long int и double. Эти типы не меняют свои размеры в зависимости от разрядности архитектуры. На 32-битной и на 64-битных системах они имеют размер 8 байт. Но выравнивание для 32-битных и 64-битных систем различно. Это как раз и может привести к изменению размера структуры. Когда мы будем реализовывать Viva64 под Linux. мы обязательно учтем возможность возникновения связанных с этим потенциальных ошибок.
В Windows системах подобных потенциальных проблем с изменением выравнивания не наблюдается. Обратите внимание, что выравнивание всех типов остается неизменным или меняется вместе с изменением размера типа. Хорошо. У Windows разработчиков одной потенциальной проблемой меньше.
Библиографический список