English | 中文 | Русский | Français
342 Тем для обсуждения
3,586 Открытых обсуждений
При переносе программного обеспечения одной из забот, которая ложится на плечи разработчика является изменение размерности типов и правил их выравнивания. Не так давно мы поддержали в анализаторе Viva64 диагностическое правило, позволяющее обнаружить структуры данных, неэффективно использующие память на 64-битных системах. Но в данном направлении еще стоит продолжать исследования и я внимательно просматриваю сообщения в форумах по этому поводу.
В этот раз мое внимание привлекло сообщение в форуме RSDN следующего содержания:
Столкнулся сегодня с одной проблемой в 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» пример кода, выводящий на печать размер типов и их выравнивание. Немного модифицировал его для 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 систем и привожу их в таблице.
Давайте изучим эту таблицу. Обратите внимание на выделенные ячейки, относящиеся к типам long long int и double. Эти типы не меняют свои размеры в зависимости от разрядности архитектуры. На 32-битной и на 64-битных системах они имеют размер 8 байт. Но выравнивание для 32-битных и 64-битных систем различно. Это как раз и может привести к изменению размера структуры. Когда мы будем реализовывать Viva64 под Linux, мы обязательно учтем возможность возникновения связанных с этим потенциальных ошибок.
В Windows системах подобных потенциальных проблем с изменением выравнивания не наблюдается. Обратите внимание, что выравнивание всех типов остается неизменным или меняется вместе с изменением размера типа. Хорошо. У Windows разработчиков одной потенциальной проблемой меньше.
| 02.10.2009 05:44
Dmitriy Vyukov
|
Кстати, расхождение с double лучится флагом -malign-double, который используется повсеместно. Проблема с long long будет лечится флагом -malign-long-long (пока имеется только в виде патча). А для студии выравнивания контролируются флагом /ZpN. |
| 02.10.2009 07:00
Andrey Karpov
|
Проблем как бы нет. А ошибки есть (например в старом коде). Исправить их легко. А вот найти далеко не всегда. Чтобы написать ключ "-malign-long-long" в начале следует узнать, что его надо написать, чтобы избежать ошибки. Не писать же его просто так на всякий случай. :-) Здесь ошибка сохранения и восстановления данных. Пусть в программе есть структуры, содержащие поля типов double и int. Программист надеется, что размер этих данных не меняется и следовательно размер структуры данных остается прежним. Размер этих данных действительно остается одинаковым на 32-битной и 64-битной системе. А вот размер структуры из-за выравнивания изменится. И механизм сохранения/восстановления начнет работать неверно. Конечно ясно, полагаться на такие вещи нельзя. Понятно, что нужно аккуратнее относится к механизму сохранения и восстановления данных. Следует использовать специальные библиотеки, писать специальный код и так далее. Но так делают в идеальном мире. А в реальных программах, имеется то, что имеется. И цель, описывая такие проблемы помочь людям как можно быстрее обнаружить дефект. |
| 02.10.2009 09:37
mt2
| Мне приходилось сталкиваться с библиотеками, где применялись трюки, в которых исходили из определенного sizeof. Очень неприятный случай :( |
| 02.10.2009 11:39
ksili
|
> Не понятно, а в чём собственно могут быть проблемы. Ну наверно, ошибки могут быть при взаимодействии систем с разной разрядностью. Например сервер на 64, а клиент на 64 бита. Чтобы передать какую-нибудь структуру серверу, клиент её может сериализовать, а сервер у себя десериализует. Из-за разных размеров структур теоретически могут поля и поехать. |
| 03.10.2009 02:53
Dmitriy Vyukov
|
А, понятно. Спасибо. Но теперь не понятно, на какой основе будут выдаваться варнинги. Для любого класса, в котором есть члены типа wchar_t, long, double? Тут нужны какие-то дополнительные ограничения, иначе ИМХО такой варнинг будет практически бесполезным... |
| 04.10.2009 23:35
Andrey Karpov
|
Мы пока не думали над этим вопросом. Возможно, даже не удастся составить удачное правило, дающее приемлемое количество ложных срабатываний. Но, по крайней мере, можно отсечь предупреждения для тех структур, которые хотя и содержат double или long long, но не меняют свой размер. Например, размер этой структуры не изменится и предупреждение выдавать нет смысла: struct MyPoint { double x; double y; double z; }; |
| 05.10.2009 02:46
Dmitriy Vyukov
|
Андрей, Вы наверное имели в виду раскладку объектов, а не размер. Т.к. размер-то может и не измениться, а вот раскладка изменится, просто паддинг будет в других местах. Кстати, а ваши инструменты учитывают обработку препроцессором? Т.к. если речь идёт о портируемости, то в мире С/C++ препроцессор - одно из основных средств (в плане определения всяких uint64_t и т.д.). |
| 05.10.2009 05:14
Andrey Karpov
|
Вы правы Дмитрий. Не должна измениться раскладка объектов. Спасибо за внимательность. :) По поводу препроцессора. Да, учитывается. При работе мы используем как препроцессированные файлы. Хотя встроенного препроцессора у нас нет, мы используем препроцессор Visual C++. |
| 05.10.2009 06:28
Dmitriy Vyukov
|
Я имел в виду не совсем это. Допустим есть что-то типа такого:
#if MSVC
# if ARCH32
typedef long my_type_t;
# else
typedef int my_type_t;
# endif
#else
# if ARCH32
typedef long long my_type_t;
# else
typedef long my_type_t;
# endif
#endif
Разговор идёт о портировании с одной платформы на другую. Но вместе с портированием скорее всего изменится и ряд определений, т.к. будут определены другие макросы. |
| 05.10.2009 06:53
Andrey Karpov
| Прошу привести пример. Пока ход мысли мне не понятен. |
| 05.10.2009 10:34
Dmitriy Vyukov
|
Я имею в виду следующее. Всё это о портировании. Т.е. вы хотите выдавать варнинги, что при портировании допустим с Windows на Linux такой-то структуры могут быть проблемы. НО на другой платформе эта структура может быть определена ПО-ДРУГОМУ. Из-за препроцессора. Допустим есть структура: struct Foo { ... my_type_t m; ... }; На текущей платформе (виндовс) my_type_t есть int. Допустим при портировании на линукс, это вызвает проблемы. Но на линуксе my_type_t уже будет не int, а long. И в таком случае проблемы уже нет, программист всё сделал правильно. Конечно, это всё можно и не учитывать, но тогда кол-во ложных срабатываний может быть слишком высоким. Что бы его снизить необходимо учитывать, что при смене платформы определения некоторых типов могут изменяться. |
| 06.10.2009 00:26
Andrey Karpov
|
"Т.е. вы хотите выдавать варнинги, что при портировании допустим с Windows на Linux такой-то структуры могут быть проблемы." Нет. Такая задача требует отдельного рассмотрения. Речь в посте шла о том, что иногда при переходе с 32-битной на 64-битную систему изменяется выравнивание данных в структурах, хотя размеры типов остаются прежними. Изменение правил выравнивания приводит к увеличению размера структуры, что потенциально может привести к ошибке. Данный вопрос бы изучен и был сделан вывод, что данная проблема не актуальна для Windows-приложений, для которых мы разрабатываем анализатор Viva64 (входящий сейчас в состав PVS-Studio). Вопрос как подобные ошибки можно диагностировать на Linux-системах или при миграции приложений с Windows на Linux мы пока не изучали. |
| 06.10.2009 03:05
Dmitriy Vyukov
| Понятно. Спасибо. |

Dmitriy Vyukov
25,462
Статусных баллов:
25,462