После долгих ожиданий в C++Builder XE3 Update 1 появилась возможность сборки 64-битных приложений. Это значит, что использующим этот инструмент разработчикам предстоит скоро столкнуться с незнакомым для них миром 64-битных ошибок.
Поддержка C++ Builder была прекращена в PVS-Studio после версии 5.20. По всем возникшим вопросам вы можете обратиться в нашу поддержку.
Это самая обыкновенная ошибка в программе. Но у неё есть особенность. Она проявляет себя только в 64-битных программах [1]. В тоже время, собрав 32-битную версию программы, вы не обнаружите эту ошибку. Продемонстрируем в начале самый простой пример:
float *buf = (float *)malloc(1000 * sizeof(float));
unsigned X = (unsigned)buf;
...
float *p = (float *)X;
Указатель какое-то время хранится в переменной типа unsigned. В 64-битной программе указатель станет 64-битным, а тип unsigned по-прежнему состоит из 32 бит. В результате будут потеряны старшие биты, и последствия работы программы станут непредсказуемы. Дополнительно такая ошибка коварна тем, что будет редко проявлять себя в 64-битной программе. Беда произойдет только тогда, когда память будет выделена за пределами первых 4 гигабайт адресного пространства.
Показанная выше 64-битная ошибка проста и не интересна. Любой компилятор предупредит о подобном опасном приведении типов. Рассмотрим более интересный случай.
float *Array = (float *)malloc(1000 * sizeof(float));
float *p = Array + 500;
unsigned A = 3;
int B = -5;
p[A + B] = 1.0f;
Код будет работать в 32-битной программе и приведёт к падению 64-битной программы. Дело в том, что выражение "A + B" имеет тип unsigned int. Оно будет равно 0xFFFFFFFEu. Это конечно некорректно в любом случае. Однако в 32-битной системе при сложении указателя и числа произойдет переполнение. В результате 32-битная программа будет работать, не смотря на ошибку.
Эту ошибку найти уже гораздо сложнее. С точки зрения большинства компиляторов в коде нет ничего подозрительного. Не заметят компиляторы ничего подозрительного и в следующем коде:
size_t Count = BigValue;
for (unsigned Index = 0; Index != Count; Index++)
{ ... }
Если в 64-битной программе значение переменной Count станет больше UINT_MAX, то возникнет вечный цикл.
Все это только вершина айсберга. Существуют 64-битные ошибки связанные с использованием в коде магических чисел (4, 32, 0xFFFFFFFF). Есть ошибки в объявлении виртуальных функций. И так далее. Подробнее с различными паттернами 64-битных ошибок можно познакомиться в статье "Коллекция примеров 64-битных ошибок".
Помимо ошибок неожиданную неприятность может доставить замедление скорости работы 64-битной версии программы по сравнению с 32-битной. Вообще, 64-битная программа должна работать быстрее [2]. Однако на практике это не всегда так.
Причины, по которым может произойти замедление:
Теперь вы узнали, что подготовка 64-битной версии программы состоит не только из перекомпиляции кода. Вы можете столкнуться со странными ошибками и замедлением работы.
Описания методов, как избежать 64-битных ошибок выходят за рамки этой статьи. Дело в том, что нюансов слишком много. В одну статью их поместить нет никакой возможности. Поэтому придётся ограничиться ссылкой.
Нами несколько лет назад было написано большое руководство посвященное разработке 64-битных приложений. В нем подробно описаны все паттерны 64-битных ошибок. Описано, как оптимизировать 64-битную программу и на что стоит обратить внимание при создании тестов.
Руководство по разработке 64-битных приложений на языке Си/Си++
Многим разработчикам, использующим Visual C++, уже известны эти материалы. Думаю, теперь они станут полезны и для тех, кто использует компилятор C++Builder. Раздел содержит информацию общего плана и будет полезен вне зависимости от используемых инструментов.
Приведённой в руководстве информации достаточно для написания корректных 64-битных программ. Но это мало поможет, если необходимо перенести на 64-битную систему большой программный проект. Перечитать весь код и выявить подозрительные места невозможно.
Для решения этой задачи в статическом анализаторе PVS-Studio существует набор диагностических правил под названием Viva64. Этот самый полный набор для выявления 64-битных ошибок среди существующих.
Теперь, когда анализатор PVS-Studio стал доступен в среде C++Builder, программисты смогут с меньшими потерями времени и сил произвести перенос кода. Да, просмотреть все диагностические сообщения тяжело. Но, во-первых, это все равно лучше, чем долгое время вылавливать непонятные и плохо воспроизводимые ошибки [3]. Во-вторых, анализатор PVS-Studio имеет развитую систему подавления ложных срабатываний и мы всегда готовы подсказать как её настроить.
Здесь вы можете скачать полнофункциональную демонстрационную версию PVS-Studio. На момент написания статьи анализатор умеет интегрироваться в Embarcadero RAD Studio XE2 и XE3 (включая Update 1). В дальнейшем, возможно, будут поддержаны и другие версии.
На самом деле, взять старый проект и попытаться собрать 64-битную версию дело неблагодарное. Поэтому многие разработчики, использующие C++Builder, захотят делать это постепенно. В начале, есть смысл подготовить код, а только потом приступить к попыткам его компиляции в 64-битном варианте. Анализатор PVS-Studio поможет заранее устранить большинство 64-битных ошибок. Он позволяет проверять не только 64-битные, но и 32-битные проекты.
На всякий случай следует уточнить, что полноценный анализ возможен только в случае 64-битного проекта. Есть множество моментов, которые затрудняют выявление 64-битных дефектов в 32-битных проектах. Самые наглядные пример это директивы препроцессора. Если какое-то участок кода не компилируется в 32-битной программе, то соответственно он не будет проверен и ошибки в нём найдены не будут.
Помимо приведённых в статье ссылок хочу порекомендовать ещё некоторые разделы нашего сайта:
Желаю вам безглючных 64-битных программ.