SlideShare uma empresa Scribd logo
1 de 17
Baixar para ler offline
Сравнение диагностических
возможностей анализаторов при
проверке 64-битного кода
Автор: Андрей Карпов

Дата: 30.05.2008


Аннотация
В статье производится сравнение специализированного статического анализатора Viva64 со
статическими анализаторами общего назначения Parasoft C++test и Gimpel Software PC-Lint.
Сравнение производится в рамках задачи переноса 32-битного Си/Си++ кода на 64-битные
системы или разработки нового кода с учетом особенностей 64-битной архитектуры.


Введение
Цель данной статьи - продемонстрировать преимущества анализатора Viva64 по сравнению с
другими продуктами, обладающими схожими функциональными возможностями. Viva64 - это
специализированный статический анализатор для верификации 64-битного Си/Си++ кода [ 1].
Сферой его применения является разработка нового 64-битного кода или перенос старого кода на
64-битные системы. На данный момент анализатор реализован для операционной системы
Windows и представляет собой подключаемый модуль к среде разработки Visual Studio 2005/2008.

Актуальность этой статьи заключается в отсутствии систематизированной информации о
возможностях современных статических анализаторов, которые заявлены как средства
диагностики 64-битных ошибок. В рамках этой статьи мы сравним три наиболее
распространенных анализатора, реализующие проверки 64-битного кода: Viva64, Parasoft C++test,
Gimpel Software PC-Lint.

Произведенные сравнения будут отражены в виде таблицы, после чего мы кратко рассмотрим
каждый из критериев оценки. Но вначале объясним некоторые понятия, которые будут
использоваться в этой статье.


1. Термины и определения

1.1. Модель данных
Под моделью данных следует понимать соотношения размерностей типов, принятых в рамках
среды разработки. Для одной операционной системы могут существовать несколько средств
разработки, придерживающихся разных моделей данных. Но обычно преобладает только одна
модель, наиболее соответствующая аппаратной и программной среде. Примером может служить
64-битная операционная система Windows, в которой родной моделью данных является LLP64. Но
для совместимости 64-битная система Windows поддерживает исполнение 32-битных программ,
которые работают в режиме модели данных ILP32LL.
В таблице N1 приведены наиболее распространенные модели данных. Нас в первую очередь в
этой таблице интересует модель данных LP64 и LLP64.




                    Таблица 1. Наиболее распространенные модели данных.

Модели данных LP64 и LLP64 отличаются только размером типа "long". Но в этом различии
заключена существенная разница в рекомендуемых методологиях в разработке программ под 64-
битные операционные системы семейства Unix и Windows. Например, для хранения указателей и
создания циклов для обработки большого количества элементов в Unix программах
рекомендуется использовать тип long или unsigned long. В тоже время в Windows программах эти
типы непригодны и вместо них следует использовать типы ptrdiff_t и size_t. Более подробно с
особенностями использования различных моделей данных можно познакомиться в статье
"Забытые проблемы разработки 64-битных программ" [2].

В данной статье мы говорим о моделях данных по той причине, что различные статические
анализаторы не всегда одинаково хорошо приспособлены как к модели данных LP64 так и LLP64.
Забегая вперед, можно сказать, что анализаторы Parasoft C++test и Gimpel Software PC-Lint лучше
приспособлены к Unix системам, чем к Windows.

1.2. Memsize-типы
Для упрощения изложения материала мы будем использовать термин memsize-тип. Термин
"memsize" возник, как попытка лаконично назвать все типы, которые способны хранить в себе
размер указателей и индексов самых больших массивов. Memsize-тип способен хранить в себе
размер максимального массива, который может быть теоретически выделен в рамках данной
архитектуры.

Под memsize-типом мы будем понимать все простые типы данных языка Си/Си++, которые на 32-
битой архитектуре имеют размер 32-бита, а на 64-битной архитектуре - 64-бита. Обратите
внимание, что в Windows системах тип long не является memsize-типом, а в Unix системах
является. Для наглядности представим основные memsize-типы в виде таблицы N2.
Таблица 2. Примеры memsize-типов.


2. Сравнительная таблица
Приступим непосредственно к сравнению статических анализаторов. Сравнительная информация
представлена в таблице N3. Список критериев оценки был составлен на основе документации к
статическим анализаторам, статьям и другим дополнительным ресурсам. Вы можете
ознакомиться с основными источниками по следующим адресам:

   •   Статья: Andrey Karpov, Evgeniy Ryzhkov. 20 issues of porting C++ code on the 64-bit platform.
   •   Parasoft C++test: C++Test User's Guide (User Items: 3264bit_xxxxxxx.rule)
   •   Gimpel Software PC-Lint: 64-bit Test (C) Checking programs against the LP64 model.
   •   Program Verification Systems Viva64: On-line help.
Сравнение диагностических возможностей анализаторов при проверке 64-битного кода
Сравнение диагностических возможностей анализаторов при проверке 64-битного кода
Таблица N3. Сравнение статических анализаторов с точки зрения поиска специфических для 64-
                                  битного кода ошибок.


3. Критерии оценки
Указанные в таблице названия критериев оценки мало о чем говорят сами по себе. Поэтому
кратко рассмотрим каждый из них. Первому критерию будет соответствовать пункт 3.1, второму -
3.2 и так далее.

Более подробную информацию о типичных ошибках при переносе программ на 64-битные
системы Вы можете получить из следующих статей: 20 ловушек переноса Си++ - кода на 64-
битную платформу [3], Проблемы тестирования 64-битных приложений [4], Разработка
ресурсоемких приложений в среде Visual C++ [5].

3.1. Использование memsize-типов в качестве фактических аргументов в
функциях с переменным количеством аргументов
Классическим примером является некорректное использование функций printf, scanf и их
разновидностей:

1) const char *invalidFormat = "%u";

    size_t value = SIZE_MAX;

    printf(invalidFormat, value);

2) char buf[9];

    sprintf(buf, "%p", pointer);

В первом случае не учитывается тот факт, что тип size_t не эквивалентен типу unsigned на 64-
битной платформе. Это приведет к выводу на печать некорректного результата, в случае если
value > UINT_MAX.

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

3.2. Использование магических констант
В некачественном коде часто встречаются магические числовые константы, наличие которых
опасно само по себе. При миграции кода на 64-битную платформу эти константы могут сделать
код неработоспособным, если участвуют в операциях вычисления адреса, размера объектов или в
битовых операциях. К основным магическим константам можно отнести: 4, 32, 0x7fffffff,
0x80000000, 0xffffffff. Пример:

size_t ArraySize = N * 4;

intptr_t *Array = (intptr_t *)malloc(ArraySize);
3.3. Хранение в double целочисленных значений, представленных
memsize-типом
Тип double, как правило, имеет размер 64-бита, и совместим со стандартом IEEE-754 на 32-битных
и 64-битных системах. Иногда в коде тип double используется для хранения и работы с
целочисленными типами:

size_t a = size_t(-1);

double b = a;

--a;

--b;

size_t c = b; // x86: a == c

                  // x64: a != c

Данный пример еще можно пытаться оправдать на 32-битной системе, где тип double способен
без потерь хранить 32-битное целое значение, так как имеет 52 значащих бита. Но при попытке
сохранить в double 64-битное целое число точное значение может быть потеряно.

3.4. Некорректная работа с операциями сдвига
Операции сдвига при невнимательном использовании могут принести много неприятностей при
переходе от 32-битной к 64-битной системе. Рассмотрим функцию, выставляющую в переменной
типа memsize, указанный бит в 1:

ptrdiff_t SetBitN(ptrdiff_t value, unsigned bitNum) {

    ptrdiff_t mask = 1 << bitNum;

    return value | mask;

}

Приведенный код работоспособен на 32-битной архитектуре и позволяет выставлять биты с
номерами от 0 до 31. После переноса программы на 64-битную платформу возникнет
необходимость выставлять биты от 0 до 63. Но вызов функции SetBitN(0, 32) вернет 0. Следует
учесть, что "1" имеет тип int и при сдвиге на 32 позиции произойдет переполнение и конечный
результат будет неверен.

3.5. Упаковка указателей не в memsize-типы
Большое количество ошибок при мигрировании на 64-битные системы связано с изменением
размера указателя по отношению к размеру обычных целых. Многие программисты в своих 32-
битных программах хранили указатели в таких типах как int и unsigned. Это конечно не корректно с
точки зрения 64-битных моделей данных. Пример:

char *p;

p = (char *) ((unsigned int)p & PAGEOFFSET);
Следует четко помнить, что для целочисленного представления указателей следует использовать
только memsize-типы. К счастью этот тип ошибок хорошо диагностируется не только статическими
анализаторами, но и компиляторами при включении соответствующих опций.

3.6. Использование memsize-типов в объединениях (union)
Особенностью объединения (union) в языке Си/Си++ является то, что для всех элементов - членов
объединения - выделяется одна и та же область памяти. Хотя доступ к этой области памяти
возможен с использованием любого из элементов, элемент для этой цели должен выбираться так,
чтобы полученный результат был осмысленным.

Следует внимательно отнестись к объединениям, имеющим в своем составе указатели и другие
члены типа memsize. Разработчик часто ошибочно предполагает, что размер memsize-типа будет
всегда равняться группе других объектов на всех архитектурах. Пример неверной функции,
реализующей табличный алгоритм для подсчета количества нулевых битов в переменной "value":

union SizetToBytesUnion {

  size_t value;

  struct {

     unsigned char b0, b1, b2, b3;

  } bytes;

} u;



SizetToBytesUnion u;

u.value = value;

size_t zeroBitsN = TranslateTable[u.bytes.b0] +

                        TranslateTable[u.bytes.b1] +

                        TranslateTable[u.bytes.b2] +

                        TranslateTable[u.bytes.b3];

3.7. Изменение типа массива
Иногда в программах необходимо (или просто удобно) представлять элементы массива в виде
элементов другого типа. Опасное и безопасное приведение типов продемонстрировано в
следующем коде:

int array[4] = { 1, 2, 3, 4 };

enum ENumbers { ZERO, ONE, TWO, THREE, FOUR };

//safe cast (for MSVC2005/2008)

ENumbers *enumPtr = (ENumbers *)(array);

cout << enumPtr[1] << " ";
//unsafe cast

size_t *sizetPtr = (size_t *)(array);

cout << sizetPtr[1] << endl;



//Output on 32-bit system: 2 2

//Output on 64 bit system: 2 17179869187

3.8. Ошибки при использовании виртуальных функций с аргументами
типа memsize.
Если у Вас в программе имеются большие иерархии наследования классов с виртуальными
функциями, то существует вероятность использования по невнимательности аргументов
различных типов, но которые фактически совпадают на 32-битной системе. Например, в базовом
классе Вы используете в качестве аргумента виртуальной функции тип size_t, а в наследнике - тип
unsigned. Соответственно, на 64-битной системе этот код будет некорректен.

Такая ошибка не обязательно кроется в сложных иерархиях наследования, и вот один из
примеров:

сlass CWinApp {

  ...

  virtual void WinHelp(DWORD_PTR dwData, UINT nCmd);

};

class CSampleApp : public CWinApp {

  ...

  virtual void WinHelp(DWORD dwData, UINT nCmd);

};

Такие ошибки могут возникать не только из-за невнимательности программиста. Приведенная в
примере ошибка возникнет, если вы разрабатывали свой код для ранних версий библиотеки MFC,
где функция WinHelp в классе CWinApp имела прототип вида:

virtual void WinHelp(DWORD dwData, UINT nCmd = HELP_CONTEXT);

Естественно, что в своем коде вы также использовали тип DWORD. В Microsoft Visual C++
2005/2008 прототип функции изменили. На 32-битной системе программа продолжит корректно
работать, так как здесь типы DWORD и DWORD_PTR совпадают. Неприятности проявляют себя в
64-битной программе. Получатся две функции с одинаковыми именами, но с различными
параметрами, в результате чего перестанет вызываться пользовательский код.

3.9. Некорректная арифметика с указателями
Рассмотрим на следующем примере:

unsigned short a16, b16, c16;
char *pointer;

...

pointer += a16 * b16 * c16;

Данный пример корректно работает с указателями, если значение выражения "a16 * b16 * c16" не
превышает UINT_MAX (4Gb). Такой код мог всегда корректно работать на 32-битной платформе,
так как программа все равно никогда бы не смогла выделить массив большего размера. На 64-
битной архитектуре размер массива превысил UINT_MAX элементов. Допустим, мы хотим
сдвинуть значение указателя на 6.000.000.000 байт, и поэтому переменные a16, b16 и c16 имеют
значения 3000, 2000 и 1000 соответственно. При вычислении выражения "a16 * b16 * c16" все
переменные, согласно правилам языка Си++, будут приведены к типу int, а уже затем будет
произведено их умножение. В ходе выполнения умножения произойдет переполнение.
Некорректный результат выражения будет расширен до типа ptrdiff_t и произойдет некорректное
вычисление указателя.

Вот еще один пример, который работоспособен в 32-битном варианте и не работоспособен в 64-
битном:

int A = -2;

unsigned B = 1;

int array[5] = { 1, 2, 3, 4, 5 };

int *ptr = array + 3;

ptr = ptr + (A + B); //Invalid pointer value on 64-bit platform

printf("%in", *ptr); //Access violation on 64-bit platform

Давайте проследим, как происходит вычисление выражения "ptr + (A + B)":

   •   Согласно правилам языка Си++ переменная A типа int приводится к типу unsigned.
   •   Происходит сложение A и B. В результате мы получаем значение 0xFFFFFFFF типа unsigned.

Затем происходит вычисление выражения "ptr + 0xFFFFFFFFu", но что из этого выйдет, будет
зависеть от размера указателя на данной архитектуре. Если сложение будет происходить в 32-
битной программе, то данное выражение будет эквивалентно "ptr - 1" и мы успешно распечатаем
число 3.

В 64-битной программе к указателю честным образом прибавится значение 0xFFFFFFFFu, в
результате чего указатель окажется далеко за пределами массива.

3.10. Некорректная индексация больших массивов
В программировании на языке Си, а затем и Си++, сложилась практика использования в качестве
индексов для работы с массивами переменных типа int и unsigned. Но время идет, и все меняется.
И вот теперь пришло время сказать: "Больше так не делайте! Используйте для индексации
(больших) массивов только memsize-типы." Пример ошибочного кода, использующего тип
unsigned:

unsigned Index = 0;
while (MyBigNumberField[Index] != id)

  Index++;

Приведенный код не сможет обработать в 64-битной программе массив, содержащий более
UINT_MAX элементов. После доступа к элементу с индексом UINT_MAX произойдет переполнение
переменной Index и мы получим вечный цикл.

Еще раз обращаем внимание Windows разработчиков, что тип long в 64-битной Windows остался
32-битным. Поэтому рекомендации Unix разработчиков использовать для длинных циклов тип
long неуместен.

3.11. Смешанное использование простых целочисленных типов и
memsize-типов
Смешанное использование memsize- и не memsize-типов в выражениях может приводить к
некорректным результатам на 64-битных системах и быть связано с изменением диапазона
входных значений. Рассмотрим ряд примеров:

size_t Count = BigValue;

for (unsigned Index = 0; Index != Count; ++Index)

{ ... }

Это пример вечного цикла, если Count > UINT_MAX. Предположим, что на 32-битных системах этот
код работал с диапазоном менее UINT_MAX итераций. Но 64-битный вариант программы может
обрабатывать больше данных и ему может потребоваться большее количество итераций.
Поскольку значения переменной Index лежат в диапазоне [0..UINT_MAX], то условие "Index !=
Count" никогда не выполнится, что и приводит к бесконечному циклу.

Приведем небольшой код, показывающий опасность неаккуратных выражений со смешанными
типами (результаты получены с использованием Microsoft Visual C++ 2005, 64-битный режим
компиляции):

int x = 100000;

int y = 100000;

int z = 100000;

intptr_t size = 1;                            // Result:

intptr_t v1 = x * y * z;                      // -1530494976

intptr_t v2 = intptr_t(x) * y * z;            // 1000000000000000

intptr_t v3 = x * y * intptr_t(z);            // 141006540800000

intptr_t v4 = size * x * y * z;               // 1000000000000000

intptr_t v5 = x * y * z * size;               // -1530494976

intptr_t v6 = size * (x * y * z);             // -1530494976

intptr_t v7 = size * (x * y) * z;             // 141006540800000
intptr_t v8 = ((size * x) * y) * z; // 1000000000000000

intptr_t v9 = size * (x * (y * z)); // -1530494976

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

intptr_t v2 = intptr_t(x) * y * z;

вовсе не гарантирует правильный результат. Оно гарантирует только то, что выражение "intptr_t(x)
* y * z" будет иметь тип intptr_t. Правильный результат, показанный этим выражением в примере
- не более чем везение.

3.12. Опасные неявные приведения типов при вызове функций
Опасность смешанного использования memsize- и не memsize-типов может присутствовать не
только в выражениях. Рассмотрим пример:

void foo(ptrdiff_t delta);

int i = -2;

unsigned k = 1;

foo(i + k);

Ранее в статье (см. Некорректная арифметика с указателями) мы уже встречались с подобной
ситуацией. Здесь некорректный результат возникает из-за неявного расширения фактического 32-
битного аргумента до 64-бит в момент вызова функции.

3.13. Опасные неявные приведения типов при возврате значения из
функции.
Опасное неявное приведение типов может возникать и при использовании оператора return.
Пример:

extern int Width, Height, Depth;

size_t GetIndex(int x, int y, int z) {

    return x + y * Width + z * Width * Height;

}

...

MyArray[GetIndex(x, y, z)] = 0.0f;

Несмотря на то, что мы возвращаем значение типа size_t, выражение "x + y * Width + z * Width *
Height" вычисляется с использованием типа int. В случае работы с большими массивами (более
INT_MAX элементов) данный код будет вести себя некорректно, и мы будем адресоваться не к тем
элементам массива MyArray, к которым рассчитываем.

3.14. Исключения
Генерирование и обработка исключений с участием целочисленных типов не является хорошей
практикой программирования на языке Си++. Для этих целей следует использовать более
информативные типы, например классы, производные от классов std::exception. Но иногда
приходится работать с менее качественным кодом, таким как показано ниже:

char *ptr1;

char *ptr2;

try {

    try {

        throw ptr2 - ptr1;

    }

    catch (int) {

        std::cout << "catch 1: on x86" << std::endl;

    }

}

catch (ptrdiff_t) {

    std::cout << "catch 2: on x64" << std::endl;

}

Следует тщательно избегать генерирования или обработку исключений с использованием
memsize-типов, так как это чревато изменением логики работы программы.

3.15. Явные приведения типов
Будьте аккуратны с явными приведениями типов. Они могут изменить логику выполнения
программы при изменении разрядности типов или спровоцировать потерю значащих битов.
Привести типовые примеры ошибок, связанных с явным приведением типов сложно, так как они
очень разнообразны и специфичны для разных программ. С некоторыми из ошибок, связанных с
явным приведением типов, Вы уже познакомились ранее. Но в целом бывает полезно
просматривать все явные приведения типов, в которых участвуют memsize-типы.

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

Такое изменение логики хранит в себе опасность. Примером может служить запись и чтение из
файла данных посредством набора функций вида:

class CMyFile {

    ...

    void Write(__int32 &value);
void Write(__int64 &value);

};

CMyFile object;

SSIZE_T value;

object.Write(value);

Данный код в зависимости от режима компиляции (32 или 64 бит) запишет в файл различное
количество байт, что может привести к нарушению совместимости форматов файлов.

3.17. Битовые поля
Если Вы используете битовые поля, то необходимо учитывать, что использование memsize-типов
повлечет изменение размеров структур и выравнивания. Но это не все. Рассмотрим тонкий
пример:

struct BitFieldStruct {

  unsigned short a:15;

  unsigned short b:13;

};

BitFieldStruct obj;

obj.a = 0x4000;

size_t addr = obj.a << 17; //Sign Extension

printf("addr 0x%Ixn", addr);



//Output on 32-bit system: 0x80000000

//Output on 64-bit system: 0xffffffff80000000

Обратите внимание, если приведенный пример скомпилировать для 64-битной системы, то в
выражении "addr = obj.a << 17;" будет присутствовать знаковое расширение, несмотря на то, что
обе переменные addr и obj.a являются беззнаковыми. Это знаковое расширение обусловлено
правилами приведения типов, которые применяются следующим образом:

1) Член структуры obj.a преобразуется из битового поля типа unsigned short в тип int. Мы получаем
тип int, а не unsigned int из-за того, что 15-битное поле помещается в 32-битное знаковое целое.

2) Выражение "obj.a << 17" имеет тип int, но оно преобразуется в ptrdiff_t и затем в size_t, перед
тем как будет присвоено переменной addr. Знаковое расширение происходит в момент
совершения преобразования из int в ptrdiff_t.
3.18. Использование жестко заданных значений при вычислении
смещений внутри структур
Опасными играми является самостоятельное вычисление адресов полей внутри структур.
Действия такого рода часто приводят к генерации некорректного кода. Диагностика типовых
ошибок такого рода представлена в анализаторе C++Test, но, к сожалению, плохо описана.

3.19. Использование типа long
Использование long-типов в кросплатформенном коде теоретически всегда опасно при переносе
кода с 32-битной на 64-битную систему. Это связано тем, что тип long имеет разный размер в двух
наиболее распространенных моделях данных - LP64 и LLP64. Данный тип проверки реализует
поиск всех long в коде приложений.

3.20. Использование макросов, мешающих компилятору проверять типы
Данная проверка реализована в C++Test. В Viva64 и PC-Lint она не реализована, но все макросы
раскрывается и все равно происходит полная проверка. Поэтому будем считать, что в Viva64 и PC-
Lint данный тип проверки также реализован.

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

struct A { long n, m; };

void foo(const struct A *p) {

  static char buf[ 8 ]; // should have used sizeof

  memcpy(buf, p, sizeof( struct A )); //Owerflow

  ...


4. Эффективность использования статических анализаторов
Об эффективности использования статических анализаторов говорить сложно. Безусловно,
методология статического анализа крайне полезна и позволяет обнаруживать большое
количество ошибок еще на этапе написания кода, что существенно сокращает этап отладки и
тестирования.

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

   1. Некоторые элементы языка Си++ затруднены для анализа. В первую очередь это касается
      кода шаблонов, так как они оперируют с различными типами данных одними и теми же
      конструкциями.
   2. Ошибки при переносе 32-битной программы на 64-битную систему могут содержаться не
      только в коде, но и быть косвенными. Хорошим примером может служить размер стека,
      который по умолчанию не изменяет в Visual Studio 2005/2008 при сборке 64-битной
      версии проекта и равняется 1 мегабайту. При работе 64-битный код может значительно
      больше заполнять стек, чем 32-битный код. Это связано с ростом размеров указателей и
других объектов, другим выравниванием. В результате 64-битной сборке программы
      может неожиданно не хватить стека во время работы.
   3. Существуют ошибки алгоритмического характера, возникшие из некоторых
      предположений относительно размерностей типов, которые изменяются в 64-битной
      системе.
   4. Ошибки могут крыться в сторонних 64-битных библиотеках.

Перечисленный список не полон, но позволяет утверждать, что некоторые ошибки могут быть
обнаружены только при запуске программы. Другими словами необходимо нагрузочное
тестирование приложений, использование динамических систем анализа (например, Compuware
BoundsChecker), юнит-тестирование, ручное тестирование и так далее.

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

Следует также понимать, что приведенная выше критика не снижает пользу от использования
статического анализа. Статический анализ является самым эффективным методом обнаружения
ошибок при переносе 32-битного кода на 64-битные системы. Он позволяет отловить
большинство ошибок за сравнительно короткое время. К преимуществам статического анализа
кода можно отнести:

   1. Возможность проверить все ветви кода, не зависимо от частоты их выполнения в реальных
      условиях.
   2. Возможность выполнить проверку еще на этапе переноса или разработки кода. Тем
      самым огромное количество ошибок исправляется еще до этапа тестирования и отладки.
      Это существенно экономит ресурсы и время. Общеизвестно, что чем раньше обнаружена
      ошибка, тем дешевле обходится ее исправление.
   3. Статический анализатор умеет обнаруживать опасные конструкции, которые программист
      склонен считать корректными, так как они показали свою работоспособность на 32-битных
      системах.
   4. Статический анализ позволяет заранее оценить качество кода с точки зрения его
      корректности для 64-битных систем и тем самым лучше спланировать ход работ.


5. Выводы
В диагностике 64-битного кода для операционной системы Windows лидирует
специализированный анализатор Viva64. В первую очередь это связано с тем, что он
ориентирован на модель данных LLP64, а во вторую тем, что в нем реализованы новые
специфические правила диагностики [1].

В диагностике 64-битного кода для операционных систем семейства Unix следует отдать
предпочтение анализатору общего назначения PC-Lint. По таблице N3 его лидерство не очень
заметно, но он реализует более важные правила, по сравнению с C++Test.


Библиографический список
   1. Евгений Рыжков. Viva64: разработка 64-битных приложений.
   2. http://www.viva64.com/art-1-1-1888185592.html
3. Андрей Карпов. Забытые проблемы разработки 64-битных программ.
   http://www.viva64.com/art-1-1-1609701563.html
4. Андрей Карпов, Евгений Рыжков. 20 ловушек переноса Си++ - кода на 64-битную
   платформу. http://www.viva64.com/art-1-1-1958348565.html
5. Андрей Карпов. Проблемы тестирования 64-битных приложений.
6. http://www.viva64.com/art-1-1-929024801.html
7. Андрей Карпов, Евгений Рыжков. Разработка ресурсоемких приложений в среде Visual C++.
   http://www.viva64.com/art-1-1-640027853.html

Mais conteúdo relacionado

Mais procurados

Урок 13. Паттерн 5. Адресная арифметика
Урок 13. Паттерн 5. Адресная арифметикаУрок 13. Паттерн 5. Адресная арифметика
Урок 13. Паттерн 5. Адресная арифметикаTatyanazaxarova
 
PVS-Studio, решение для разработки современных ресурсоемких приложений
PVS-Studio, решение для разработки современных ресурсоемких приложенийPVS-Studio, решение для разработки современных ресурсоемких приложений
PVS-Studio, решение для разработки современных ресурсоемких приложенийTatyanazaxarova
 
Урок 10. Паттерн 2. Функции с переменным количеством аргументов
Урок 10. Паттерн 2. Функции с переменным количеством аргументовУрок 10. Паттерн 2. Функции с переменным количеством аргументов
Урок 10. Паттерн 2. Функции с переменным количеством аргументовTatyanazaxarova
 
Урок 5. Сборка 64-битного приложения
Урок 5. Сборка 64-битного приложенияУрок 5. Сборка 64-битного приложения
Урок 5. Сборка 64-битного приложенияTatyanazaxarova
 
Большой брат помогает тебе
Большой брат помогает тебеБольшой брат помогает тебе
Большой брат помогает тебеTatyanazaxarova
 
Урок 26. Оптимизация 64-битных программ
Урок 26. Оптимизация 64-битных программУрок 26. Оптимизация 64-битных программ
Урок 26. Оптимизация 64-битных программTatyanazaxarova
 
Урок 6. Ошибки в 64-битном коде
Урок 6. Ошибки в 64-битном кодеУрок 6. Ошибки в 64-битном коде
Урок 6. Ошибки в 64-битном кодеTatyanazaxarova
 
Урок 19. Паттерн 11. Сериализация и обмен данными
Урок 19. Паттерн 11. Сериализация и обмен даннымиУрок 19. Паттерн 11. Сериализация и обмен данными
Урок 19. Паттерн 11. Сериализация и обмен даннымиTatyanazaxarova
 
C++ осень 2012 лекция 6
C++ осень 2012 лекция 6C++ осень 2012 лекция 6
C++ осень 2012 лекция 6Technopark
 
Урок 9. Паттерн 1. Магические числа
Урок 9. Паттерн 1. Магические числаУрок 9. Паттерн 1. Магические числа
Урок 9. Паттерн 1. Магические числаTatyanazaxarova
 
C++ осень 2013 лекция 2
C++ осень 2013 лекция 2C++ осень 2013 лекция 2
C++ осень 2013 лекция 2Technopark
 
Урок 15. Паттерн 7. Упаковка указателей
Урок 15. Паттерн 7. Упаковка указателейУрок 15. Паттерн 7. Упаковка указателей
Урок 15. Паттерн 7. Упаковка указателейTatyanazaxarova
 
C++ весна 2014 лекция 2
C++ весна 2014 лекция 2C++ весна 2014 лекция 2
C++ весна 2014 лекция 2Technopark
 
C++ осень 2013 лекция 6
C++ осень 2013 лекция 6C++ осень 2013 лекция 6
C++ осень 2013 лекция 6Technopark
 
Урок 17. Паттерн 9. Смешанная арифметика
Урок 17. Паттерн 9. Смешанная арифметикаУрок 17. Паттерн 9. Смешанная арифметика
Урок 17. Паттерн 9. Смешанная арифметикаTatyanazaxarova
 

Mais procurados (15)

Урок 13. Паттерн 5. Адресная арифметика
Урок 13. Паттерн 5. Адресная арифметикаУрок 13. Паттерн 5. Адресная арифметика
Урок 13. Паттерн 5. Адресная арифметика
 
PVS-Studio, решение для разработки современных ресурсоемких приложений
PVS-Studio, решение для разработки современных ресурсоемких приложенийPVS-Studio, решение для разработки современных ресурсоемких приложений
PVS-Studio, решение для разработки современных ресурсоемких приложений
 
Урок 10. Паттерн 2. Функции с переменным количеством аргументов
Урок 10. Паттерн 2. Функции с переменным количеством аргументовУрок 10. Паттерн 2. Функции с переменным количеством аргументов
Урок 10. Паттерн 2. Функции с переменным количеством аргументов
 
Урок 5. Сборка 64-битного приложения
Урок 5. Сборка 64-битного приложенияУрок 5. Сборка 64-битного приложения
Урок 5. Сборка 64-битного приложения
 
Большой брат помогает тебе
Большой брат помогает тебеБольшой брат помогает тебе
Большой брат помогает тебе
 
Урок 26. Оптимизация 64-битных программ
Урок 26. Оптимизация 64-битных программУрок 26. Оптимизация 64-битных программ
Урок 26. Оптимизация 64-битных программ
 
Урок 6. Ошибки в 64-битном коде
Урок 6. Ошибки в 64-битном кодеУрок 6. Ошибки в 64-битном коде
Урок 6. Ошибки в 64-битном коде
 
Урок 19. Паттерн 11. Сериализация и обмен данными
Урок 19. Паттерн 11. Сериализация и обмен даннымиУрок 19. Паттерн 11. Сериализация и обмен данными
Урок 19. Паттерн 11. Сериализация и обмен данными
 
C++ осень 2012 лекция 6
C++ осень 2012 лекция 6C++ осень 2012 лекция 6
C++ осень 2012 лекция 6
 
Урок 9. Паттерн 1. Магические числа
Урок 9. Паттерн 1. Магические числаУрок 9. Паттерн 1. Магические числа
Урок 9. Паттерн 1. Магические числа
 
C++ осень 2013 лекция 2
C++ осень 2013 лекция 2C++ осень 2013 лекция 2
C++ осень 2013 лекция 2
 
Урок 15. Паттерн 7. Упаковка указателей
Урок 15. Паттерн 7. Упаковка указателейУрок 15. Паттерн 7. Упаковка указателей
Урок 15. Паттерн 7. Упаковка указателей
 
C++ весна 2014 лекция 2
C++ весна 2014 лекция 2C++ весна 2014 лекция 2
C++ весна 2014 лекция 2
 
C++ осень 2013 лекция 6
C++ осень 2013 лекция 6C++ осень 2013 лекция 6
C++ осень 2013 лекция 6
 
Урок 17. Паттерн 9. Смешанная арифметика
Урок 17. Паттерн 9. Смешанная арифметикаУрок 17. Паттерн 9. Смешанная арифметика
Урок 17. Паттерн 9. Смешанная арифметика
 

Destaque

Bao thuc tap tot nghiep vo minh truong 10299191
Bao thuc tap tot nghiep vo minh truong 10299191Bao thuc tap tot nghiep vo minh truong 10299191
Bao thuc tap tot nghiep vo minh truong 10299191Cu Sock
 
Silvio Meira_apresentação Seminário Educação para o Século 21
Silvio Meira_apresentação Seminário Educação para o Século 21Silvio Meira_apresentação Seminário Educação para o Século 21
Silvio Meira_apresentação Seminário Educação para o Século 21Lah Nascimento
 
Cloud Identity: A Recipe for Higher Education
Cloud Identity: A Recipe for Higher EducationCloud Identity: A Recipe for Higher Education
Cloud Identity: A Recipe for Higher EducationMike Schwartz
 
Syarifudin, outline dakwah dan komunikasi 2012
Syarifudin, outline dakwah dan komunikasi 2012Syarifudin, outline dakwah dan komunikasi 2012
Syarifudin, outline dakwah dan komunikasi 2012Syarifudin Amq
 
Baja 2013 17._proposta_de_alteracao_rbsb_-_liberacao_da_rotacao_limite_do_mot...
Baja 2013 17._proposta_de_alteracao_rbsb_-_liberacao_da_rotacao_limite_do_mot...Baja 2013 17._proposta_de_alteracao_rbsb_-_liberacao_da_rotacao_limite_do_mot...
Baja 2013 17._proposta_de_alteracao_rbsb_-_liberacao_da_rotacao_limite_do_mot...Jonatas Ferreira
 

Destaque (7)

Bao thuc tap tot nghiep vo minh truong 10299191
Bao thuc tap tot nghiep vo minh truong 10299191Bao thuc tap tot nghiep vo minh truong 10299191
Bao thuc tap tot nghiep vo minh truong 10299191
 
Silvio Meira_apresentação Seminário Educação para o Século 21
Silvio Meira_apresentação Seminário Educação para o Século 21Silvio Meira_apresentação Seminário Educação para o Século 21
Silvio Meira_apresentação Seminário Educação para o Século 21
 
Cloud Identity: A Recipe for Higher Education
Cloud Identity: A Recipe for Higher EducationCloud Identity: A Recipe for Higher Education
Cloud Identity: A Recipe for Higher Education
 
Life aftercalcday2
Life aftercalcday2Life aftercalcday2
Life aftercalcday2
 
Syarifudin, outline dakwah dan komunikasi 2012
Syarifudin, outline dakwah dan komunikasi 2012Syarifudin, outline dakwah dan komunikasi 2012
Syarifudin, outline dakwah dan komunikasi 2012
 
Baja 2013 17._proposta_de_alteracao_rbsb_-_liberacao_da_rotacao_limite_do_mot...
Baja 2013 17._proposta_de_alteracao_rbsb_-_liberacao_da_rotacao_limite_do_mot...Baja 2013 17._proposta_de_alteracao_rbsb_-_liberacao_da_rotacao_limite_do_mot...
Baja 2013 17._proposta_de_alteracao_rbsb_-_liberacao_da_rotacao_limite_do_mot...
 
Tecnocom - Presentacion corporativa
Tecnocom - Presentacion corporativaTecnocom - Presentacion corporativa
Tecnocom - Presentacion corporativa
 

Semelhante a Сравнение диагностических возможностей анализаторов при проверке 64-битного кода

Забытые проблемы разработки 64-битных программ
Забытые проблемы разработки 64-битных программЗабытые проблемы разработки 64-битных программ
Забытые проблемы разработки 64-битных программTatyanazaxarova
 
Особенности разработки 64-битных приложений
Особенности разработки 64-битных приложенийОсобенности разработки 64-битных приложений
Особенности разработки 64-битных приложенийTatyanazaxarova
 
Оптимизация 64-битных программ
Оптимизация 64-битных программОптимизация 64-битных программ
Оптимизация 64-битных программTatyanazaxarova
 
Урок 8. Статический анализ для выявления 64-битных ошибок
Урок 8. Статический анализ для выявления 64-битных ошибокУрок 8. Статический анализ для выявления 64-битных ошибок
Урок 8. Статический анализ для выявления 64-битных ошибокTatyanazaxarova
 
Реклама PVS-Studio - статический анализ кода на языке Си и Си++
Реклама PVS-Studio - статический анализ кода на языке Си и Си++Реклама PVS-Studio - статический анализ кода на языке Си и Си++
Реклама PVS-Studio - статический анализ кода на языке Си и Си++Andrey Karpov
 
Урок 23. Паттерн 15. Рост размеров структур
Урок 23. Паттерн 15. Рост размеров структурУрок 23. Паттерн 15. Рост размеров структур
Урок 23. Паттерн 15. Рост размеров структурTatyanazaxarova
 
Разница в подходах анализа кода компилятором и выделенным инструментом
Разница в подходах анализа кода компилятором и выделенным инструментомРазница в подходах анализа кода компилятором и выделенным инструментом
Разница в подходах анализа кода компилятором и выделенным инструментомTatyanazaxarova
 
Трудности сравнения анализаторов кода или не забывайте об удобстве использования
Трудности сравнения анализаторов кода или не забывайте об удобстве использованияТрудности сравнения анализаторов кода или не забывайте об удобстве использования
Трудности сравнения анализаторов кода или не забывайте об удобстве использованияTatyanazaxarova
 
Применение статического анализа кода в преподавании и в разработке свободного ПО
Применение статического анализа кода в преподавании и в разработке свободного ПОПрименение статического анализа кода в преподавании и в разработке свободного ПО
Применение статического анализа кода в преподавании и в разработке свободного ПОAndrey Karpov
 
Статический анализ исходного кода на примере WinMerge
Статический анализ исходного кода на примере WinMergeСтатический анализ исходного кода на примере WinMerge
Статический анализ исходного кода на примере WinMergeTatyanazaxarova
 
Разработка ресурсоемких приложений в среде Visual C++
Разработка ресурсоемких приложений в среде Visual C++Разработка ресурсоемких приложений в среде Visual C++
Разработка ресурсоемких приложений в среде Visual C++Tatyanazaxarova
 
Урок 11. Паттерн 3. Операции сдвига
Урок 11. Паттерн 3. Операции сдвигаУрок 11. Паттерн 3. Операции сдвига
Урок 11. Паттерн 3. Операции сдвигаTatyanazaxarova
 
Регулярное использование статического анализа кода в командной разработке
Регулярное использование статического анализа кода в командной разработкеРегулярное использование статического анализа кода в командной разработке
Регулярное использование статического анализа кода в командной разработкеTatyanazaxarova
 
Интервью с Анатолием Кузнецовым, автором библиотеки BitMagic C++ Library
Интервью с Анатолием Кузнецовым, автором библиотеки BitMagic C++ LibraryИнтервью с Анатолием Кузнецовым, автором библиотеки BitMagic C++ Library
Интервью с Анатолием Кузнецовым, автором библиотеки BitMagic C++ LibraryTatyanazaxarova
 
Отладка и оптимизация многопоточных OpenMP-программ
Отладка и оптимизация многопоточных OpenMP-программОтладка и оптимизация многопоточных OpenMP-программ
Отладка и оптимизация многопоточных OpenMP-программTatyanazaxarova
 
Что такое "Parallel Lint"?
Что такое "Parallel Lint"?Что такое "Parallel Lint"?
Что такое "Parallel Lint"?Tatyanazaxarova
 
Паттерны 64-битных ошибок в играх
Паттерны 64-битных ошибок в играхПаттерны 64-битных ошибок в играх
Паттерны 64-битных ошибок в играхAndrey Karpov
 
Статический анализ Си++ кода и новый стандарт языка C++0x
Статический анализ Си++ кода и новый стандарт языка C++0xСтатический анализ Си++ кода и новый стандарт языка C++0x
Статический анализ Си++ кода и новый стандарт языка C++0xTatyanazaxarova
 

Semelhante a Сравнение диагностических возможностей анализаторов при проверке 64-битного кода (20)

Забытые проблемы разработки 64-битных программ
Забытые проблемы разработки 64-битных программЗабытые проблемы разработки 64-битных программ
Забытые проблемы разработки 64-битных программ
 
Особенности разработки 64-битных приложений
Особенности разработки 64-битных приложенийОсобенности разработки 64-битных приложений
Особенности разработки 64-битных приложений
 
Оптимизация 64-битных программ
Оптимизация 64-битных программОптимизация 64-битных программ
Оптимизация 64-битных программ
 
Урок 8. Статический анализ для выявления 64-битных ошибок
Урок 8. Статический анализ для выявления 64-битных ошибокУрок 8. Статический анализ для выявления 64-битных ошибок
Урок 8. Статический анализ для выявления 64-битных ошибок
 
Реклама PVS-Studio - статический анализ кода на языке Си и Си++
Реклама PVS-Studio - статический анализ кода на языке Си и Си++Реклама PVS-Studio - статический анализ кода на языке Си и Си++
Реклама PVS-Studio - статический анализ кода на языке Си и Си++
 
Урок 23. Паттерн 15. Рост размеров структур
Урок 23. Паттерн 15. Рост размеров структурУрок 23. Паттерн 15. Рост размеров структур
Урок 23. Паттерн 15. Рост размеров структур
 
Разница в подходах анализа кода компилятором и выделенным инструментом
Разница в подходах анализа кода компилятором и выделенным инструментомРазница в подходах анализа кода компилятором и выделенным инструментом
Разница в подходах анализа кода компилятором и выделенным инструментом
 
Трудности сравнения анализаторов кода или не забывайте об удобстве использования
Трудности сравнения анализаторов кода или не забывайте об удобстве использованияТрудности сравнения анализаторов кода или не забывайте об удобстве использования
Трудности сравнения анализаторов кода или не забывайте об удобстве использования
 
Применение статического анализа кода в преподавании и в разработке свободного ПО
Применение статического анализа кода в преподавании и в разработке свободного ПОПрименение статического анализа кода в преподавании и в разработке свободного ПО
Применение статического анализа кода в преподавании и в разработке свободного ПО
 
Статический анализ исходного кода на примере WinMerge
Статический анализ исходного кода на примере WinMergeСтатический анализ исходного кода на примере WinMerge
Статический анализ исходного кода на примере WinMerge
 
Разработка ресурсоемких приложений в среде Visual C++
Разработка ресурсоемких приложений в среде Visual C++Разработка ресурсоемких приложений в среде Visual C++
Разработка ресурсоемких приложений в среде Visual C++
 
паскаль 10кл 14
паскаль 10кл 14паскаль 10кл 14
паскаль 10кл 14
 
PVS-Studio vs Chromium
PVS-Studio vs ChromiumPVS-Studio vs Chromium
PVS-Studio vs Chromium
 
Урок 11. Паттерн 3. Операции сдвига
Урок 11. Паттерн 3. Операции сдвигаУрок 11. Паттерн 3. Операции сдвига
Урок 11. Паттерн 3. Операции сдвига
 
Регулярное использование статического анализа кода в командной разработке
Регулярное использование статического анализа кода в командной разработкеРегулярное использование статического анализа кода в командной разработке
Регулярное использование статического анализа кода в командной разработке
 
Интервью с Анатолием Кузнецовым, автором библиотеки BitMagic C++ Library
Интервью с Анатолием Кузнецовым, автором библиотеки BitMagic C++ LibraryИнтервью с Анатолием Кузнецовым, автором библиотеки BitMagic C++ Library
Интервью с Анатолием Кузнецовым, автором библиотеки BitMagic C++ Library
 
Отладка и оптимизация многопоточных OpenMP-программ
Отладка и оптимизация многопоточных OpenMP-программОтладка и оптимизация многопоточных OpenMP-программ
Отладка и оптимизация многопоточных OpenMP-программ
 
Что такое "Parallel Lint"?
Что такое "Parallel Lint"?Что такое "Parallel Lint"?
Что такое "Parallel Lint"?
 
Паттерны 64-битных ошибок в играх
Паттерны 64-битных ошибок в играхПаттерны 64-битных ошибок в играх
Паттерны 64-битных ошибок в играх
 
Статический анализ Си++ кода и новый стандарт языка C++0x
Статический анализ Си++ кода и новый стандарт языка C++0xСтатический анализ Си++ кода и новый стандарт языка C++0x
Статический анализ Си++ кода и новый стандарт языка C++0x
 

Mais de Tatyanazaxarova

Урок 27. Особенности создания инсталляторов для 64-битного окружения
Урок 27. Особенности создания инсталляторов для 64-битного окруженияУрок 27. Особенности создания инсталляторов для 64-битного окружения
Урок 27. Особенности создания инсталляторов для 64-битного окруженияTatyanazaxarova
 
Урок 25. Практическое знакомство с паттернами 64-битных ошибок
Урок 25. Практическое знакомство с паттернами 64-битных ошибокУрок 25. Практическое знакомство с паттернами 64-битных ошибок
Урок 25. Практическое знакомство с паттернами 64-битных ошибокTatyanazaxarova
 
Урок 24. Фантомные ошибки
Урок 24. Фантомные ошибкиУрок 24. Фантомные ошибки
Урок 24. Фантомные ошибкиTatyanazaxarova
 
Урок 21. Паттерн 13. Выравнивание данных
Урок 21. Паттерн 13. Выравнивание данныхУрок 21. Паттерн 13. Выравнивание данных
Урок 21. Паттерн 13. Выравнивание данныхTatyanazaxarova
 
Урок 20. Паттерн 12. Исключения
Урок 20. Паттерн 12. ИсключенияУрок 20. Паттерн 12. Исключения
Урок 20. Паттерн 12. ИсключенияTatyanazaxarova
 
Урок 16. Паттерн 8. Memsize-типы в объединениях
Урок 16. Паттерн 8. Memsize-типы в объединенияхУрок 16. Паттерн 8. Memsize-типы в объединениях
Урок 16. Паттерн 8. Memsize-типы в объединенияхTatyanazaxarova
 
Урок 7. Проблемы выявления 64-битных ошибок
Урок 7. Проблемы выявления 64-битных ошибокУрок 7. Проблемы выявления 64-битных ошибок
Урок 7. Проблемы выявления 64-битных ошибокTatyanazaxarova
 
Урок 4. Создание 64-битной конфигурации
Урок 4. Создание 64-битной конфигурацииУрок 4. Создание 64-битной конфигурации
Урок 4. Создание 64-битной конфигурацииTatyanazaxarova
 
Статический анализ Си++ кода
Статический анализ Си++ кодаСтатический анализ Си++ кода
Статический анализ Си++ кодаTatyanazaxarova
 
PVS-Studio научился следить за тем, как вы программируете
PVS-Studio научился следить за тем, как вы программируетеPVS-Studio научился следить за тем, как вы программируете
PVS-Studio научился следить за тем, как вы программируетеTatyanazaxarova
 
Пояснения к статье про Copy-Paste
Пояснения к статье про Copy-PasteПояснения к статье про Copy-Paste
Пояснения к статье про Copy-PasteTatyanazaxarova
 
Использование анализатора PVS-Studio в процессе инкрементальной сборки в Micr...
Использование анализатора PVS-Studio в процессе инкрементальной сборки в Micr...Использование анализатора PVS-Studio в процессе инкрементальной сборки в Micr...
Использование анализатора PVS-Studio в процессе инкрементальной сборки в Micr...Tatyanazaxarova
 
Статический анализ и ROI
Статический анализ и ROIСтатический анализ и ROI
Статический анализ и ROITatyanazaxarova
 
Вечный вопрос измерения времени
Вечный вопрос измерения времениВечный вопрос измерения времени
Вечный вопрос измерения времениTatyanazaxarova
 
По колено в Си++ г... коде
По колено в Си++ г... кодеПо колено в Си++ г... коде
По колено в Си++ г... кодеTatyanazaxarova
 
Статический анализ и регулярные выражения
Статический анализ и регулярные выраженияСтатический анализ и регулярные выражения
Статический анализ и регулярные выраженияTatyanazaxarova
 

Mais de Tatyanazaxarova (17)

Урок 27. Особенности создания инсталляторов для 64-битного окружения
Урок 27. Особенности создания инсталляторов для 64-битного окруженияУрок 27. Особенности создания инсталляторов для 64-битного окружения
Урок 27. Особенности создания инсталляторов для 64-битного окружения
 
Урок 25. Практическое знакомство с паттернами 64-битных ошибок
Урок 25. Практическое знакомство с паттернами 64-битных ошибокУрок 25. Практическое знакомство с паттернами 64-битных ошибок
Урок 25. Практическое знакомство с паттернами 64-битных ошибок
 
Урок 24. Фантомные ошибки
Урок 24. Фантомные ошибкиУрок 24. Фантомные ошибки
Урок 24. Фантомные ошибки
 
Урок 21. Паттерн 13. Выравнивание данных
Урок 21. Паттерн 13. Выравнивание данныхУрок 21. Паттерн 13. Выравнивание данных
Урок 21. Паттерн 13. Выравнивание данных
 
Урок 20. Паттерн 12. Исключения
Урок 20. Паттерн 12. ИсключенияУрок 20. Паттерн 12. Исключения
Урок 20. Паттерн 12. Исключения
 
Урок 16. Паттерн 8. Memsize-типы в объединениях
Урок 16. Паттерн 8. Memsize-типы в объединенияхУрок 16. Паттерн 8. Memsize-типы в объединениях
Урок 16. Паттерн 8. Memsize-типы в объединениях
 
Урок 7. Проблемы выявления 64-битных ошибок
Урок 7. Проблемы выявления 64-битных ошибокУрок 7. Проблемы выявления 64-битных ошибок
Урок 7. Проблемы выявления 64-битных ошибок
 
Урок 4. Создание 64-битной конфигурации
Урок 4. Создание 64-битной конфигурацииУрок 4. Создание 64-битной конфигурации
Урок 4. Создание 64-битной конфигурации
 
Статический анализ Си++ кода
Статический анализ Си++ кодаСтатический анализ Си++ кода
Статический анализ Си++ кода
 
PVS-Studio
PVS-Studio PVS-Studio
PVS-Studio
 
PVS-Studio научился следить за тем, как вы программируете
PVS-Studio научился следить за тем, как вы программируетеPVS-Studio научился следить за тем, как вы программируете
PVS-Studio научился следить за тем, как вы программируете
 
Пояснения к статье про Copy-Paste
Пояснения к статье про Copy-PasteПояснения к статье про Copy-Paste
Пояснения к статье про Copy-Paste
 
Использование анализатора PVS-Studio в процессе инкрементальной сборки в Micr...
Использование анализатора PVS-Studio в процессе инкрементальной сборки в Micr...Использование анализатора PVS-Studio в процессе инкрементальной сборки в Micr...
Использование анализатора PVS-Studio в процессе инкрементальной сборки в Micr...
 
Статический анализ и ROI
Статический анализ и ROIСтатический анализ и ROI
Статический анализ и ROI
 
Вечный вопрос измерения времени
Вечный вопрос измерения времениВечный вопрос измерения времени
Вечный вопрос измерения времени
 
По колено в Си++ г... коде
По колено в Си++ г... кодеПо колено в Си++ г... коде
По колено в Си++ г... коде
 
Статический анализ и регулярные выражения
Статический анализ и регулярные выраженияСтатический анализ и регулярные выражения
Статический анализ и регулярные выражения
 

Сравнение диагностических возможностей анализаторов при проверке 64-битного кода

  • 1. Сравнение диагностических возможностей анализаторов при проверке 64-битного кода Автор: Андрей Карпов Дата: 30.05.2008 Аннотация В статье производится сравнение специализированного статического анализатора Viva64 со статическими анализаторами общего назначения Parasoft C++test и Gimpel Software PC-Lint. Сравнение производится в рамках задачи переноса 32-битного Си/Си++ кода на 64-битные системы или разработки нового кода с учетом особенностей 64-битной архитектуры. Введение Цель данной статьи - продемонстрировать преимущества анализатора Viva64 по сравнению с другими продуктами, обладающими схожими функциональными возможностями. Viva64 - это специализированный статический анализатор для верификации 64-битного Си/Си++ кода [ 1]. Сферой его применения является разработка нового 64-битного кода или перенос старого кода на 64-битные системы. На данный момент анализатор реализован для операционной системы Windows и представляет собой подключаемый модуль к среде разработки Visual Studio 2005/2008. Актуальность этой статьи заключается в отсутствии систематизированной информации о возможностях современных статических анализаторов, которые заявлены как средства диагностики 64-битных ошибок. В рамках этой статьи мы сравним три наиболее распространенных анализатора, реализующие проверки 64-битного кода: Viva64, Parasoft C++test, Gimpel Software PC-Lint. Произведенные сравнения будут отражены в виде таблицы, после чего мы кратко рассмотрим каждый из критериев оценки. Но вначале объясним некоторые понятия, которые будут использоваться в этой статье. 1. Термины и определения 1.1. Модель данных Под моделью данных следует понимать соотношения размерностей типов, принятых в рамках среды разработки. Для одной операционной системы могут существовать несколько средств разработки, придерживающихся разных моделей данных. Но обычно преобладает только одна модель, наиболее соответствующая аппаратной и программной среде. Примером может служить 64-битная операционная система Windows, в которой родной моделью данных является LLP64. Но для совместимости 64-битная система Windows поддерживает исполнение 32-битных программ, которые работают в режиме модели данных ILP32LL.
  • 2. В таблице N1 приведены наиболее распространенные модели данных. Нас в первую очередь в этой таблице интересует модель данных LP64 и LLP64. Таблица 1. Наиболее распространенные модели данных. Модели данных LP64 и LLP64 отличаются только размером типа "long". Но в этом различии заключена существенная разница в рекомендуемых методологиях в разработке программ под 64- битные операционные системы семейства Unix и Windows. Например, для хранения указателей и создания циклов для обработки большого количества элементов в Unix программах рекомендуется использовать тип long или unsigned long. В тоже время в Windows программах эти типы непригодны и вместо них следует использовать типы ptrdiff_t и size_t. Более подробно с особенностями использования различных моделей данных можно познакомиться в статье "Забытые проблемы разработки 64-битных программ" [2]. В данной статье мы говорим о моделях данных по той причине, что различные статические анализаторы не всегда одинаково хорошо приспособлены как к модели данных LP64 так и LLP64. Забегая вперед, можно сказать, что анализаторы Parasoft C++test и Gimpel Software PC-Lint лучше приспособлены к Unix системам, чем к Windows. 1.2. Memsize-типы Для упрощения изложения материала мы будем использовать термин memsize-тип. Термин "memsize" возник, как попытка лаконично назвать все типы, которые способны хранить в себе размер указателей и индексов самых больших массивов. Memsize-тип способен хранить в себе размер максимального массива, который может быть теоретически выделен в рамках данной архитектуры. Под memsize-типом мы будем понимать все простые типы данных языка Си/Си++, которые на 32- битой архитектуре имеют размер 32-бита, а на 64-битной архитектуре - 64-бита. Обратите внимание, что в Windows системах тип long не является memsize-типом, а в Unix системах является. Для наглядности представим основные memsize-типы в виде таблицы N2.
  • 3. Таблица 2. Примеры memsize-типов. 2. Сравнительная таблица Приступим непосредственно к сравнению статических анализаторов. Сравнительная информация представлена в таблице N3. Список критериев оценки был составлен на основе документации к статическим анализаторам, статьям и другим дополнительным ресурсам. Вы можете ознакомиться с основными источниками по следующим адресам: • Статья: Andrey Karpov, Evgeniy Ryzhkov. 20 issues of porting C++ code on the 64-bit platform. • Parasoft C++test: C++Test User's Guide (User Items: 3264bit_xxxxxxx.rule) • Gimpel Software PC-Lint: 64-bit Test (C) Checking programs against the LP64 model. • Program Verification Systems Viva64: On-line help.
  • 6. Таблица N3. Сравнение статических анализаторов с точки зрения поиска специфических для 64- битного кода ошибок. 3. Критерии оценки Указанные в таблице названия критериев оценки мало о чем говорят сами по себе. Поэтому кратко рассмотрим каждый из них. Первому критерию будет соответствовать пункт 3.1, второму - 3.2 и так далее. Более подробную информацию о типичных ошибках при переносе программ на 64-битные системы Вы можете получить из следующих статей: 20 ловушек переноса Си++ - кода на 64- битную платформу [3], Проблемы тестирования 64-битных приложений [4], Разработка ресурсоемких приложений в среде Visual C++ [5]. 3.1. Использование memsize-типов в качестве фактических аргументов в функциях с переменным количеством аргументов Классическим примером является некорректное использование функций printf, scanf и их разновидностей: 1) const char *invalidFormat = "%u"; size_t value = SIZE_MAX; printf(invalidFormat, value); 2) char buf[9]; sprintf(buf, "%p", pointer); В первом случае не учитывается тот факт, что тип size_t не эквивалентен типу unsigned на 64- битной платформе. Это приведет к выводу на печать некорректного результата, в случае если value > UINT_MAX. Во втором случае не учитывается, что размер указателя в будущем может составить более 32 бит. В результате на 64-битной архитектуре данный код приведет к переполнению буфера. 3.2. Использование магических констант В некачественном коде часто встречаются магические числовые константы, наличие которых опасно само по себе. При миграции кода на 64-битную платформу эти константы могут сделать код неработоспособным, если участвуют в операциях вычисления адреса, размера объектов или в битовых операциях. К основным магическим константам можно отнести: 4, 32, 0x7fffffff, 0x80000000, 0xffffffff. Пример: size_t ArraySize = N * 4; intptr_t *Array = (intptr_t *)malloc(ArraySize);
  • 7. 3.3. Хранение в double целочисленных значений, представленных memsize-типом Тип double, как правило, имеет размер 64-бита, и совместим со стандартом IEEE-754 на 32-битных и 64-битных системах. Иногда в коде тип double используется для хранения и работы с целочисленными типами: size_t a = size_t(-1); double b = a; --a; --b; size_t c = b; // x86: a == c // x64: a != c Данный пример еще можно пытаться оправдать на 32-битной системе, где тип double способен без потерь хранить 32-битное целое значение, так как имеет 52 значащих бита. Но при попытке сохранить в double 64-битное целое число точное значение может быть потеряно. 3.4. Некорректная работа с операциями сдвига Операции сдвига при невнимательном использовании могут принести много неприятностей при переходе от 32-битной к 64-битной системе. Рассмотрим функцию, выставляющую в переменной типа memsize, указанный бит в 1: ptrdiff_t SetBitN(ptrdiff_t value, unsigned bitNum) { ptrdiff_t mask = 1 << bitNum; return value | mask; } Приведенный код работоспособен на 32-битной архитектуре и позволяет выставлять биты с номерами от 0 до 31. После переноса программы на 64-битную платформу возникнет необходимость выставлять биты от 0 до 63. Но вызов функции SetBitN(0, 32) вернет 0. Следует учесть, что "1" имеет тип int и при сдвиге на 32 позиции произойдет переполнение и конечный результат будет неверен. 3.5. Упаковка указателей не в memsize-типы Большое количество ошибок при мигрировании на 64-битные системы связано с изменением размера указателя по отношению к размеру обычных целых. Многие программисты в своих 32- битных программах хранили указатели в таких типах как int и unsigned. Это конечно не корректно с точки зрения 64-битных моделей данных. Пример: char *p; p = (char *) ((unsigned int)p & PAGEOFFSET);
  • 8. Следует четко помнить, что для целочисленного представления указателей следует использовать только memsize-типы. К счастью этот тип ошибок хорошо диагностируется не только статическими анализаторами, но и компиляторами при включении соответствующих опций. 3.6. Использование memsize-типов в объединениях (union) Особенностью объединения (union) в языке Си/Си++ является то, что для всех элементов - членов объединения - выделяется одна и та же область памяти. Хотя доступ к этой области памяти возможен с использованием любого из элементов, элемент для этой цели должен выбираться так, чтобы полученный результат был осмысленным. Следует внимательно отнестись к объединениям, имеющим в своем составе указатели и другие члены типа memsize. Разработчик часто ошибочно предполагает, что размер memsize-типа будет всегда равняться группе других объектов на всех архитектурах. Пример неверной функции, реализующей табличный алгоритм для подсчета количества нулевых битов в переменной "value": union SizetToBytesUnion { size_t value; struct { unsigned char b0, b1, b2, b3; } bytes; } u; SizetToBytesUnion u; u.value = value; size_t zeroBitsN = TranslateTable[u.bytes.b0] + TranslateTable[u.bytes.b1] + TranslateTable[u.bytes.b2] + TranslateTable[u.bytes.b3]; 3.7. Изменение типа массива Иногда в программах необходимо (или просто удобно) представлять элементы массива в виде элементов другого типа. Опасное и безопасное приведение типов продемонстрировано в следующем коде: int array[4] = { 1, 2, 3, 4 }; enum ENumbers { ZERO, ONE, TWO, THREE, FOUR }; //safe cast (for MSVC2005/2008) ENumbers *enumPtr = (ENumbers *)(array); cout << enumPtr[1] << " ";
  • 9. //unsafe cast size_t *sizetPtr = (size_t *)(array); cout << sizetPtr[1] << endl; //Output on 32-bit system: 2 2 //Output on 64 bit system: 2 17179869187 3.8. Ошибки при использовании виртуальных функций с аргументами типа memsize. Если у Вас в программе имеются большие иерархии наследования классов с виртуальными функциями, то существует вероятность использования по невнимательности аргументов различных типов, но которые фактически совпадают на 32-битной системе. Например, в базовом классе Вы используете в качестве аргумента виртуальной функции тип size_t, а в наследнике - тип unsigned. Соответственно, на 64-битной системе этот код будет некорректен. Такая ошибка не обязательно кроется в сложных иерархиях наследования, и вот один из примеров: сlass CWinApp { ... virtual void WinHelp(DWORD_PTR dwData, UINT nCmd); }; class CSampleApp : public CWinApp { ... virtual void WinHelp(DWORD dwData, UINT nCmd); }; Такие ошибки могут возникать не только из-за невнимательности программиста. Приведенная в примере ошибка возникнет, если вы разрабатывали свой код для ранних версий библиотеки MFC, где функция WinHelp в классе CWinApp имела прототип вида: virtual void WinHelp(DWORD dwData, UINT nCmd = HELP_CONTEXT); Естественно, что в своем коде вы также использовали тип DWORD. В Microsoft Visual C++ 2005/2008 прототип функции изменили. На 32-битной системе программа продолжит корректно работать, так как здесь типы DWORD и DWORD_PTR совпадают. Неприятности проявляют себя в 64-битной программе. Получатся две функции с одинаковыми именами, но с различными параметрами, в результате чего перестанет вызываться пользовательский код. 3.9. Некорректная арифметика с указателями Рассмотрим на следующем примере: unsigned short a16, b16, c16;
  • 10. char *pointer; ... pointer += a16 * b16 * c16; Данный пример корректно работает с указателями, если значение выражения "a16 * b16 * c16" не превышает UINT_MAX (4Gb). Такой код мог всегда корректно работать на 32-битной платформе, так как программа все равно никогда бы не смогла выделить массив большего размера. На 64- битной архитектуре размер массива превысил UINT_MAX элементов. Допустим, мы хотим сдвинуть значение указателя на 6.000.000.000 байт, и поэтому переменные a16, b16 и c16 имеют значения 3000, 2000 и 1000 соответственно. При вычислении выражения "a16 * b16 * c16" все переменные, согласно правилам языка Си++, будут приведены к типу int, а уже затем будет произведено их умножение. В ходе выполнения умножения произойдет переполнение. Некорректный результат выражения будет расширен до типа ptrdiff_t и произойдет некорректное вычисление указателя. Вот еще один пример, который работоспособен в 32-битном варианте и не работоспособен в 64- битном: int A = -2; unsigned B = 1; int array[5] = { 1, 2, 3, 4, 5 }; int *ptr = array + 3; ptr = ptr + (A + B); //Invalid pointer value on 64-bit platform printf("%in", *ptr); //Access violation on 64-bit platform Давайте проследим, как происходит вычисление выражения "ptr + (A + B)": • Согласно правилам языка Си++ переменная A типа int приводится к типу unsigned. • Происходит сложение A и B. В результате мы получаем значение 0xFFFFFFFF типа unsigned. Затем происходит вычисление выражения "ptr + 0xFFFFFFFFu", но что из этого выйдет, будет зависеть от размера указателя на данной архитектуре. Если сложение будет происходить в 32- битной программе, то данное выражение будет эквивалентно "ptr - 1" и мы успешно распечатаем число 3. В 64-битной программе к указателю честным образом прибавится значение 0xFFFFFFFFu, в результате чего указатель окажется далеко за пределами массива. 3.10. Некорректная индексация больших массивов В программировании на языке Си, а затем и Си++, сложилась практика использования в качестве индексов для работы с массивами переменных типа int и unsigned. Но время идет, и все меняется. И вот теперь пришло время сказать: "Больше так не делайте! Используйте для индексации (больших) массивов только memsize-типы." Пример ошибочного кода, использующего тип unsigned: unsigned Index = 0;
  • 11. while (MyBigNumberField[Index] != id) Index++; Приведенный код не сможет обработать в 64-битной программе массив, содержащий более UINT_MAX элементов. После доступа к элементу с индексом UINT_MAX произойдет переполнение переменной Index и мы получим вечный цикл. Еще раз обращаем внимание Windows разработчиков, что тип long в 64-битной Windows остался 32-битным. Поэтому рекомендации Unix разработчиков использовать для длинных циклов тип long неуместен. 3.11. Смешанное использование простых целочисленных типов и memsize-типов Смешанное использование memsize- и не memsize-типов в выражениях может приводить к некорректным результатам на 64-битных системах и быть связано с изменением диапазона входных значений. Рассмотрим ряд примеров: size_t Count = BigValue; for (unsigned Index = 0; Index != Count; ++Index) { ... } Это пример вечного цикла, если Count > UINT_MAX. Предположим, что на 32-битных системах этот код работал с диапазоном менее UINT_MAX итераций. Но 64-битный вариант программы может обрабатывать больше данных и ему может потребоваться большее количество итераций. Поскольку значения переменной Index лежат в диапазоне [0..UINT_MAX], то условие "Index != Count" никогда не выполнится, что и приводит к бесконечному циклу. Приведем небольшой код, показывающий опасность неаккуратных выражений со смешанными типами (результаты получены с использованием Microsoft Visual C++ 2005, 64-битный режим компиляции): int x = 100000; int y = 100000; int z = 100000; intptr_t size = 1; // Result: intptr_t v1 = x * y * z; // -1530494976 intptr_t v2 = intptr_t(x) * y * z; // 1000000000000000 intptr_t v3 = x * y * intptr_t(z); // 141006540800000 intptr_t v4 = size * x * y * z; // 1000000000000000 intptr_t v5 = x * y * z * size; // -1530494976 intptr_t v6 = size * (x * y * z); // -1530494976 intptr_t v7 = size * (x * y) * z; // 141006540800000
  • 12. intptr_t v8 = ((size * x) * y) * z; // 1000000000000000 intptr_t v9 = size * (x * (y * z)); // -1530494976 Необходимо, чтобы все операнды в подобных выражениях были заранее приведены к типу большей разрядности. Помните, что выражение вида intptr_t v2 = intptr_t(x) * y * z; вовсе не гарантирует правильный результат. Оно гарантирует только то, что выражение "intptr_t(x) * y * z" будет иметь тип intptr_t. Правильный результат, показанный этим выражением в примере - не более чем везение. 3.12. Опасные неявные приведения типов при вызове функций Опасность смешанного использования memsize- и не memsize-типов может присутствовать не только в выражениях. Рассмотрим пример: void foo(ptrdiff_t delta); int i = -2; unsigned k = 1; foo(i + k); Ранее в статье (см. Некорректная арифметика с указателями) мы уже встречались с подобной ситуацией. Здесь некорректный результат возникает из-за неявного расширения фактического 32- битного аргумента до 64-бит в момент вызова функции. 3.13. Опасные неявные приведения типов при возврате значения из функции. Опасное неявное приведение типов может возникать и при использовании оператора return. Пример: extern int Width, Height, Depth; size_t GetIndex(int x, int y, int z) { return x + y * Width + z * Width * Height; } ... MyArray[GetIndex(x, y, z)] = 0.0f; Несмотря на то, что мы возвращаем значение типа size_t, выражение "x + y * Width + z * Width * Height" вычисляется с использованием типа int. В случае работы с большими массивами (более INT_MAX элементов) данный код будет вести себя некорректно, и мы будем адресоваться не к тем элементам массива MyArray, к которым рассчитываем. 3.14. Исключения Генерирование и обработка исключений с участием целочисленных типов не является хорошей практикой программирования на языке Си++. Для этих целей следует использовать более
  • 13. информативные типы, например классы, производные от классов std::exception. Но иногда приходится работать с менее качественным кодом, таким как показано ниже: char *ptr1; char *ptr2; try { try { throw ptr2 - ptr1; } catch (int) { std::cout << "catch 1: on x86" << std::endl; } } catch (ptrdiff_t) { std::cout << "catch 2: on x64" << std::endl; } Следует тщательно избегать генерирования или обработку исключений с использованием memsize-типов, так как это чревато изменением логики работы программы. 3.15. Явные приведения типов Будьте аккуратны с явными приведениями типов. Они могут изменить логику выполнения программы при изменении разрядности типов или спровоцировать потерю значащих битов. Привести типовые примеры ошибок, связанных с явным приведением типов сложно, так как они очень разнообразны и специфичны для разных программ. С некоторыми из ошибок, связанных с явным приведением типов, Вы уже познакомились ранее. Но в целом бывает полезно просматривать все явные приведения типов, в которых участвуют memsize-типы. 3.16. Перегруженные функции При переносе 32-битных программ на 64-битную платформу может наблюдаться изменение логики ее работы, связанное с использованием перегруженных функций. Если функция перекрыта для 32-битных и 64-битных значений, то обращение к ней с аргументом типа memsize будет транслироваться в различные вызовы на различных системах. Такое изменение логики хранит в себе опасность. Примером может служить запись и чтение из файла данных посредством набора функций вида: class CMyFile { ... void Write(__int32 &value);
  • 14. void Write(__int64 &value); }; CMyFile object; SSIZE_T value; object.Write(value); Данный код в зависимости от режима компиляции (32 или 64 бит) запишет в файл различное количество байт, что может привести к нарушению совместимости форматов файлов. 3.17. Битовые поля Если Вы используете битовые поля, то необходимо учитывать, что использование memsize-типов повлечет изменение размеров структур и выравнивания. Но это не все. Рассмотрим тонкий пример: struct BitFieldStruct { unsigned short a:15; unsigned short b:13; }; BitFieldStruct obj; obj.a = 0x4000; size_t addr = obj.a << 17; //Sign Extension printf("addr 0x%Ixn", addr); //Output on 32-bit system: 0x80000000 //Output on 64-bit system: 0xffffffff80000000 Обратите внимание, если приведенный пример скомпилировать для 64-битной системы, то в выражении "addr = obj.a << 17;" будет присутствовать знаковое расширение, несмотря на то, что обе переменные addr и obj.a являются беззнаковыми. Это знаковое расширение обусловлено правилами приведения типов, которые применяются следующим образом: 1) Член структуры obj.a преобразуется из битового поля типа unsigned short в тип int. Мы получаем тип int, а не unsigned int из-за того, что 15-битное поле помещается в 32-битное знаковое целое. 2) Выражение "obj.a << 17" имеет тип int, но оно преобразуется в ptrdiff_t и затем в size_t, перед тем как будет присвоено переменной addr. Знаковое расширение происходит в момент совершения преобразования из int в ptrdiff_t.
  • 15. 3.18. Использование жестко заданных значений при вычислении смещений внутри структур Опасными играми является самостоятельное вычисление адресов полей внутри структур. Действия такого рода часто приводят к генерации некорректного кода. Диагностика типовых ошибок такого рода представлена в анализаторе C++Test, но, к сожалению, плохо описана. 3.19. Использование типа long Использование long-типов в кросплатформенном коде теоретически всегда опасно при переносе кода с 32-битной на 64-битную систему. Это связано тем, что тип long имеет разный размер в двух наиболее распространенных моделях данных - LP64 и LLP64. Данный тип проверки реализует поиск всех long в коде приложений. 3.20. Использование макросов, мешающих компилятору проверять типы Данная проверка реализована в C++Test. В Viva64 и PC-Lint она не реализована, но все макросы раскрывается и все равно происходит полная проверка. Поэтому будем считать, что в Viva64 и PC- Lint данный тип проверки также реализован. 3.21. Переполнение массивов с явно заданным размером Иногда можно обнаружить переполнение массива, которое произойдет при переходе на 64- битную архитектуру. Например: struct A { long n, m; }; void foo(const struct A *p) { static char buf[ 8 ]; // should have used sizeof memcpy(buf, p, sizeof( struct A )); //Owerflow ... 4. Эффективность использования статических анализаторов Об эффективности использования статических анализаторов говорить сложно. Безусловно, методология статического анализа крайне полезна и позволяет обнаруживать большое количество ошибок еще на этапе написания кода, что существенно сокращает этап отладки и тестирования. Но следует понимать, что статический анализ никогда не позволит выловить все ошибки, даже в конкретной области, которой является анализ 64-битного кода. Перечислим основные причины: 1. Некоторые элементы языка Си++ затруднены для анализа. В первую очередь это касается кода шаблонов, так как они оперируют с различными типами данных одними и теми же конструкциями. 2. Ошибки при переносе 32-битной программы на 64-битную систему могут содержаться не только в коде, но и быть косвенными. Хорошим примером может служить размер стека, который по умолчанию не изменяет в Visual Studio 2005/2008 при сборке 64-битной версии проекта и равняется 1 мегабайту. При работе 64-битный код может значительно больше заполнять стек, чем 32-битный код. Это связано с ростом размеров указателей и
  • 16. других объектов, другим выравниванием. В результате 64-битной сборке программы может неожиданно не хватить стека во время работы. 3. Существуют ошибки алгоритмического характера, возникшие из некоторых предположений относительно размерностей типов, которые изменяются в 64-битной системе. 4. Ошибки могут крыться в сторонних 64-битных библиотеках. Перечисленный список не полон, но позволяет утверждать, что некоторые ошибки могут быть обнаружены только при запуске программы. Другими словами необходимо нагрузочное тестирование приложений, использование динамических систем анализа (например, Compuware BoundsChecker), юнит-тестирование, ручное тестирование и так далее. Таким образом, гарантию качества 64-битной программы может дать только комплексный подход к ее проверке, когда используются различные методологии и инструменты. Следует также понимать, что приведенная выше критика не снижает пользу от использования статического анализа. Статический анализ является самым эффективным методом обнаружения ошибок при переносе 32-битного кода на 64-битные системы. Он позволяет отловить большинство ошибок за сравнительно короткое время. К преимуществам статического анализа кода можно отнести: 1. Возможность проверить все ветви кода, не зависимо от частоты их выполнения в реальных условиях. 2. Возможность выполнить проверку еще на этапе переноса или разработки кода. Тем самым огромное количество ошибок исправляется еще до этапа тестирования и отладки. Это существенно экономит ресурсы и время. Общеизвестно, что чем раньше обнаружена ошибка, тем дешевле обходится ее исправление. 3. Статический анализатор умеет обнаруживать опасные конструкции, которые программист склонен считать корректными, так как они показали свою работоспособность на 32-битных системах. 4. Статический анализ позволяет заранее оценить качество кода с точки зрения его корректности для 64-битных систем и тем самым лучше спланировать ход работ. 5. Выводы В диагностике 64-битного кода для операционной системы Windows лидирует специализированный анализатор Viva64. В первую очередь это связано с тем, что он ориентирован на модель данных LLP64, а во вторую тем, что в нем реализованы новые специфические правила диагностики [1]. В диагностике 64-битного кода для операционных систем семейства Unix следует отдать предпочтение анализатору общего назначения PC-Lint. По таблице N3 его лидерство не очень заметно, но он реализует более важные правила, по сравнению с C++Test. Библиографический список 1. Евгений Рыжков. Viva64: разработка 64-битных приложений. 2. http://www.viva64.com/art-1-1-1888185592.html
  • 17. 3. Андрей Карпов. Забытые проблемы разработки 64-битных программ. http://www.viva64.com/art-1-1-1609701563.html 4. Андрей Карпов, Евгений Рыжков. 20 ловушек переноса Си++ - кода на 64-битную платформу. http://www.viva64.com/art-1-1-1958348565.html 5. Андрей Карпов. Проблемы тестирования 64-битных приложений. 6. http://www.viva64.com/art-1-1-929024801.html 7. Андрей Карпов, Евгений Рыжков. Разработка ресурсоемких приложений в среде Visual C++. http://www.viva64.com/art-1-1-640027853.html