SlideShare a Scribd company logo
Урок 9. Паттерн 1. Магические числа
В некачественном коде часто встречаются магические числовые константы, наличие которых
опасно само по себе. При миграции кода на 64-битную платформу эти константы могут сделать
код неработоспособным, если участвуют в операциях вычисления адреса, размера объектов или в
битовых операциях.

В таблице 1 перечислены основные магические константы, которые могут влиять на
работоспособность приложения на новой платформе.




 Таблица 1 - Основные магические значения, опасные при переносе приложений с 32-битной на
                                   64-битную платформу

Следует внимательно изучить код на предмет наличия магических констант и заменить их
безопасными константами и выражениями. Для этого можно использовать оператор sizeof(),
специальные значения из <limits.h>, <inttypes.h> и так далее.

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

1) size_t ArraySize = N * 4;

   intptr_t *Array = (intptr_t *)malloc(ArraySize);

2) size_t values[ARRAY_SIZE];

   memset(values, 0, ARRAY_SIZE * 4);

3) size_t n, r;

   n = n >> (32 - r);

Во всех случаях предполагаем, что размер используемых типов всегда равен 4 байта. Исправление
кода заключается в использовании оператора sizeof():
1) size_t ArraySize = N * sizeof(intptr_t);

      intptr_t *Array = (intptr_t *)malloc(ArraySize);

2) size_t values[ARRAY_SIZE];

      memset(values, 0, ARRAY_SIZE * sizeof(size_t));

или

      memset(values, 0, sizeof(values)); //preferred alternative

3) size_t n, r;

      n = n >> (CHAR_BIT * sizeof(n) - r);

Иногда может потребоваться специфическая константа. В качестве примера мы возьмем значение
size_t, где все биты кроме 4 младших должны быть заполнены единицами. В 32-битной
программе эта константа может быть объявлена следующим образом:

// constant '1111..110000'

const size_t M = 0xFFFFFFF0u;

Это некорректный код в случае 64-битной системы. Такие ошибки очень неприятны, так как запись
магических констант может быть осуществлена различными способами и их поиск достаточно
трудоемок. К сожалению, нет никаких других путей, кроме как найти и исправить этот код,
используя директиву #ifdef или специальный макрос.

#ifdef _WIN64

  #define CONST3264(a) (a##i64)

#else

  #define CONST3264(a)         (a)

#endif

const size_t M = ~CONST3264(0xFu);

Иногда в качестве кода ошибки или другого специального маркера используют значение "-1",
записывая его как "0xffffffff". На 64-битной платформе записанное выражение некорректно и
следует явно использовать значение -1. Пример некорректного кода, использующего значение
0xffffffff как признак ошибки:

#define INVALID_RESULT (0xFFFFFFFFu)

size_t MyStrLen(const char *str) {

  if (str == NULL)

      return INVALID_RESULT;

  ...

  return n;
}

size_t len = MyStrLen(str);

if (len == (size_t)(-1))

    ShowError();

На всякий случай уточним, чему равно значение "(size_t)(-1)" на 64-битной платформе. Можно
ошибиться, назвав значение 0x00000000FFFFFFFFu. Согласно правилам языка Си++ сначала
значение -1 преобразуется в знаковый эквивалент большего типа, а затем в беззнаковое
значение:

int a = -1;                  // 0xFFFFFFFFi32

ptrdiff_t b = a;             // 0xFFFFFFFFFFFFFFFFi64

size_t c = size_t(b); // 0xFFFFFFFFFFFFFFFFui64

Таким образом, "(size_t)(-1)" на 64-битной архитектуре представляется значением
0xFFFFFFFFFFFFFFFFui64, которое является максимальным значением для 64-битного типа size_t.

Вернемся к ошибке с INVALID_RESULT. Использование константы 0xFFFFFFFFu приводит к
невыполнению условия "len == (size_t)(-1)" в 64-битной программе. Наилучшее решение
заключается в изменении кода так, чтобы специальных маркерных значений не требовалось. Если
по какой-то причине Вы не можете от них отказаться или считаете нецелесообразным
существенные правки кода, то просто используйте честное значение -1.

#define INVALID_RESULT (size_t(-1))

...

Приведем еще один пример связанный с использованием 0xFFFFFFFF. Код взят из реального
приложения для трёхмерного моделирования:

hFileMapping = CreateFileMapping (

      (HANDLE) 0xFFFFFFFF,

      NULL,

      PAGE_READWRITE,

      (DWORD) 0,

      (DWORD) (szBufIm),

      (LPCTSTR) &FileShareNameMap[0]);

Как вы уже правильно догадались, 0xFFFFFFFF здесь также приведет к ошибке на 64-битной
системе. Первый аргумент функции CreateFileMapping может иметь значение
INVALID_HANDLE_VALUE, объявленное следующим образом:

#define INVALID_HANDLE_VALUE ((HANDLE)(LONG_PTR)-1)
В результате INVALID_HANDLE_VALUE действительно совпадает в 32-битной системе со значением
0xFFFFFFFF. А вот в 64-битной системе в функцию CreateFileMapping будет передано значение
0x00000000FFFFFFFF, в результате чего система посчитает аргумент некорректным и вернет код
ошибки. Причина в том, что значение 0xFFFFFFFF имеет БЕЗЗНАКОВЫЙ тип (unsigned int). Значение
0xFFFFFFFF не помещается в тип int и поэтому является типом unsigned. Это тонкий момент, на
который следует обратить внимание при переходе на 64-битные системы. Поясним его на
примере:

void foo(void *ptr)

{

    cout << ptr << endl;

}

int _tmain(int, _TCHAR *[])

{

    cout << "-1tt";

    foo((void *)-1);

    cout << "0xFFFFFFFFt";

    foo((void *)0xFFFFFFFF);

}

Результат работы 32-битного варианта программы:

-1                   FFFFFFFF

0xFFFFFFFF           FFFFFFFF

Результат работы 64-битного варианта программы:

-1                   FFFFFFFFFFFFFFFF

0xFFFFFFFF           00000000FFFFFFFF


Диагностика
Статический анализатор PVS-Studio предупреждает о наличии в коде магических констант,
имеющих наибольшую опасность при создании 64-битного приложения. Для этого используются
диагностические сообщения V112 и V118. Учтите, анализатор сознательно не предупреждает о
потенциальной ошибке, если магическая константа определена через макрос. Пример:

#define MB_YESNO        0x00000004L

MessageBox("Are you sure ?", "Question", MB_YESNO);

Если совсем кратко, то причина такого поведения - защита от огромного количества ложных
срабатываний. При этом считается, что если программист задает константу через макрос, то он
делает это специально, чтобы подчеркнуть ее безопасность. Подробнее с данным вопросом
можно познакомиться в записи блога на нашем сайте "Магические константы и функция malloc()".

Авторы курса: Андрей Карпов (karpov@viva64.com), Евгений Рыжков (evg@viva64.com).

Правообладателем курса "Уроки разработки 64-битных приложений на языке Си/Си++"
является ООО "Системы программной верификации". Компания занимается разработкой
программного обеспечения в области анализа исходного кода программ. Сайт компании:
http://www.viva64.com.

Контактная информация: e-mail: support@viva64.com, 300027, г. Тула, а/я 1800.

More Related Content

PDF
Урок 17. Паттерн 9. Смешанная арифметика
PDF
Урок 13. Паттерн 5. Адресная арифметика
PDF
Урок 15. Паттерн 7. Упаковка указателей
PDF
Урок 11. Паттерн 3. Операции сдвига
PDF
Урок 20. Паттерн 12. Исключения
PDF
Что такое size_t и ptrdiff_t
PDF
Безопасность 64-битного кода
PPT
лабораторная работа №2
Урок 17. Паттерн 9. Смешанная арифметика
Урок 13. Паттерн 5. Адресная арифметика
Урок 15. Паттерн 7. Упаковка указателей
Урок 11. Паттерн 3. Операции сдвига
Урок 20. Паттерн 12. Исключения
Что такое size_t и ptrdiff_t
Безопасность 64-битного кода
лабораторная работа №2

What's hot (19)

PDF
64-битный конь, который умеет считать
PDF
Введение в синтаксис C++, часть 1
PDF
Урок 5. Сборка 64-битного приложения
PDF
Урок 10. Паттерн 2. Функции с переменным количеством аргументов
PDF
Разработка статического анализатора кода для обнаружения ошибок переноса прог...
PDF
Сравнение диагностических возможностей анализаторов при проверке 64-битного кода
PDF
Как стандарт C++0x поможет в борьбе с 64-битными ошибками
PPT
практика 7
PPTX
PVS-Studio, решение для разработки современных ресурсоемких приложений
PPT
Типы данных
PPT
лабораторная работа 3
PDF
20 ловушек переноса Си++ - кода на 64-битную платформу
PPT
курсовой проект
PDF
Коллекция примеров 64-битных ошибок в реальных программах
PPT
лабораторная работа 4
PPT
Владислав Шаклеин. Смешивание управляемого и неуправляемого C++ кода в Micros...
PDF
Оптимизация в мире 64-битных ошибок
PDF
Правила статического анализа кода для диагностики потенциально опасных констр...
64-битный конь, который умеет считать
Введение в синтаксис C++, часть 1
Урок 5. Сборка 64-битного приложения
Урок 10. Паттерн 2. Функции с переменным количеством аргументов
Разработка статического анализатора кода для обнаружения ошибок переноса прог...
Сравнение диагностических возможностей анализаторов при проверке 64-битного кода
Как стандарт C++0x поможет в борьбе с 64-битными ошибками
практика 7
PVS-Studio, решение для разработки современных ресурсоемких приложений
Типы данных
лабораторная работа 3
20 ловушек переноса Си++ - кода на 64-битную платформу
курсовой проект
Коллекция примеров 64-битных ошибок в реальных программах
лабораторная работа 4
Владислав Шаклеин. Смешивание управляемого и неуправляемого C++ кода в Micros...
Оптимизация в мире 64-битных ошибок
Правила статического анализа кода для диагностики потенциально опасных констр...
Ad

Viewers also liked (19)

PDF
Competencias y Resultados de Apredizajes
PDF
90352163 exodus-21-commentary
PDF
גיליון 92 - יולי 2014
DOCX
ολα λυσία 9-11
PDF
耳鼻咽喉科頭頸部外科9月号2011
PDF
навигатор 93
PPT
9 24 12 leccion
PDF
9240895 curso-de-excel-pdf
DOCX
PDF
9211 -unidad_4_-_lengua_castellana_y_literatura_1_eso-actividades
PPTX
9.1 Group 1
PDF
海潮音雜誌94卷目錄
DOC
92911 product worklog
DOCX
940/2 - Ujian Sekolah Kedua, Mei 2012 - SMK Sacred Heart
PPT
90 M Overview
DOCX
PPT
第9章 符号表
PDF
920i Brochure
DOC
ใบงาน 9.1
Competencias y Resultados de Apredizajes
90352163 exodus-21-commentary
גיליון 92 - יולי 2014
ολα λυσία 9-11
耳鼻咽喉科頭頸部外科9月号2011
навигатор 93
9 24 12 leccion
9240895 curso-de-excel-pdf
9211 -unidad_4_-_lengua_castellana_y_literatura_1_eso-actividades
9.1 Group 1
海潮音雜誌94卷目錄
92911 product worklog
940/2 - Ujian Sekolah Kedua, Mei 2012 - SMK Sacred Heart
90 M Overview
第9章 符号表
920i Brochure
ใบงาน 9.1
Ad

Similar to Урок 9. Паттерн 1. Магические числа (19)

PPTX
PVS-Studio, решение для разработки современных ресурсоемких приложений
PDF
Step cpp0201
PDF
Урок 6. Ошибки в 64-битном коде
PDF
Статический анализ исходного кода на примере WinMerge
PDF
Статический анализ кода для верификации 64-битных приложений
PDF
Семинар 11. Параллельное программирование на MPI (часть 4)
PPTX
ADD 2011: Статический анализ Си++ кода
PPTX
Статический анализ Си++ кода
PDF
Урок 21. Паттерн 13. Выравнивание данных
PPTX
На что нужно обратить внимание при обзоре кода разрабатываемой библиотеки
PDF
Урок 23. Паттерн 15. Рост размеров структур
PDF
Урок 24. Фантомные ошибки
PDF
Разница в подходах анализа кода компилятором и выделенным инструментом
PDF
Урок 26. Оптимизация 64-битных программ
PDF
Особенности разработки 64-битных приложений
PDF
Как уменьшить вероятность ошибки на этапе написания кода. Заметка N1.
PPTX
Принципы работы статического анализатора кода PVS-Studio
PPTX
Паттерны 64-битных ошибок в играх
PDF
Статический анализ Си++ кода и новый стандарт языка C++0x
PVS-Studio, решение для разработки современных ресурсоемких приложений
Step cpp0201
Урок 6. Ошибки в 64-битном коде
Статический анализ исходного кода на примере WinMerge
Статический анализ кода для верификации 64-битных приложений
Семинар 11. Параллельное программирование на MPI (часть 4)
ADD 2011: Статический анализ Си++ кода
Статический анализ Си++ кода
Урок 21. Паттерн 13. Выравнивание данных
На что нужно обратить внимание при обзоре кода разрабатываемой библиотеки
Урок 23. Паттерн 15. Рост размеров структур
Урок 24. Фантомные ошибки
Разница в подходах анализа кода компилятором и выделенным инструментом
Урок 26. Оптимизация 64-битных программ
Особенности разработки 64-битных приложений
Как уменьшить вероятность ошибки на этапе написания кода. Заметка N1.
Принципы работы статического анализатора кода PVS-Studio
Паттерны 64-битных ошибок в играх
Статический анализ Си++ кода и новый стандарт языка C++0x

More from Tatyanazaxarova (16)

PDF
Урок 27. Особенности создания инсталляторов для 64-битного окружения
PDF
Урок 25. Практическое знакомство с паттернами 64-битных ошибок
PDF
Урок 19. Паттерн 11. Сериализация и обмен данными
PDF
Урок 16. Паттерн 8. Memsize-типы в объединениях
PDF
Урок 8. Статический анализ для выявления 64-битных ошибок
PDF
Урок 7. Проблемы выявления 64-битных ошибок
PDF
Урок 4. Создание 64-битной конфигурации
PDF
PVS-Studio
PDF
PVS-Studio научился следить за тем, как вы программируете
PDF
Пояснения к статье про Copy-Paste
PDF
Использование анализатора PVS-Studio в процессе инкрементальной сборки в Micr...
PDF
Статический анализ и ROI
PDF
Вечный вопрос измерения времени
PDF
По колено в Си++ г... коде
PDF
Статический анализ и регулярные выражения
PDF
Трепещи, мир! Мы выпустили PVS-Studio 4.00 с бесплатным анализатором общего н...
Урок 27. Особенности создания инсталляторов для 64-битного окружения
Урок 25. Практическое знакомство с паттернами 64-битных ошибок
Урок 19. Паттерн 11. Сериализация и обмен данными
Урок 16. Паттерн 8. Memsize-типы в объединениях
Урок 8. Статический анализ для выявления 64-битных ошибок
Урок 7. Проблемы выявления 64-битных ошибок
Урок 4. Создание 64-битной конфигурации
PVS-Studio
PVS-Studio научился следить за тем, как вы программируете
Пояснения к статье про Copy-Paste
Использование анализатора PVS-Studio в процессе инкрементальной сборки в Micr...
Статический анализ и ROI
Вечный вопрос измерения времени
По колено в Си++ г... коде
Статический анализ и регулярные выражения
Трепещи, мир! Мы выпустили PVS-Studio 4.00 с бесплатным анализатором общего н...

Урок 9. Паттерн 1. Магические числа

  • 1. Урок 9. Паттерн 1. Магические числа В некачественном коде часто встречаются магические числовые константы, наличие которых опасно само по себе. При миграции кода на 64-битную платформу эти константы могут сделать код неработоспособным, если участвуют в операциях вычисления адреса, размера объектов или в битовых операциях. В таблице 1 перечислены основные магические константы, которые могут влиять на работоспособность приложения на новой платформе. Таблица 1 - Основные магические значения, опасные при переносе приложений с 32-битной на 64-битную платформу Следует внимательно изучить код на предмет наличия магических констант и заменить их безопасными константами и выражениями. Для этого можно использовать оператор sizeof(), специальные значения из <limits.h>, <inttypes.h> и так далее. Приведем несколько ошибок, связанных с использованием магических констант. Самой распространенной является запись в виде числовых значений размеров типов: 1) size_t ArraySize = N * 4; intptr_t *Array = (intptr_t *)malloc(ArraySize); 2) size_t values[ARRAY_SIZE]; memset(values, 0, ARRAY_SIZE * 4); 3) size_t n, r; n = n >> (32 - r); Во всех случаях предполагаем, что размер используемых типов всегда равен 4 байта. Исправление кода заключается в использовании оператора sizeof():
  • 2. 1) size_t ArraySize = N * sizeof(intptr_t); intptr_t *Array = (intptr_t *)malloc(ArraySize); 2) size_t values[ARRAY_SIZE]; memset(values, 0, ARRAY_SIZE * sizeof(size_t)); или memset(values, 0, sizeof(values)); //preferred alternative 3) size_t n, r; n = n >> (CHAR_BIT * sizeof(n) - r); Иногда может потребоваться специфическая константа. В качестве примера мы возьмем значение size_t, где все биты кроме 4 младших должны быть заполнены единицами. В 32-битной программе эта константа может быть объявлена следующим образом: // constant '1111..110000' const size_t M = 0xFFFFFFF0u; Это некорректный код в случае 64-битной системы. Такие ошибки очень неприятны, так как запись магических констант может быть осуществлена различными способами и их поиск достаточно трудоемок. К сожалению, нет никаких других путей, кроме как найти и исправить этот код, используя директиву #ifdef или специальный макрос. #ifdef _WIN64 #define CONST3264(a) (a##i64) #else #define CONST3264(a) (a) #endif const size_t M = ~CONST3264(0xFu); Иногда в качестве кода ошибки или другого специального маркера используют значение "-1", записывая его как "0xffffffff". На 64-битной платформе записанное выражение некорректно и следует явно использовать значение -1. Пример некорректного кода, использующего значение 0xffffffff как признак ошибки: #define INVALID_RESULT (0xFFFFFFFFu) size_t MyStrLen(const char *str) { if (str == NULL) return INVALID_RESULT; ... return n;
  • 3. } size_t len = MyStrLen(str); if (len == (size_t)(-1)) ShowError(); На всякий случай уточним, чему равно значение "(size_t)(-1)" на 64-битной платформе. Можно ошибиться, назвав значение 0x00000000FFFFFFFFu. Согласно правилам языка Си++ сначала значение -1 преобразуется в знаковый эквивалент большего типа, а затем в беззнаковое значение: int a = -1; // 0xFFFFFFFFi32 ptrdiff_t b = a; // 0xFFFFFFFFFFFFFFFFi64 size_t c = size_t(b); // 0xFFFFFFFFFFFFFFFFui64 Таким образом, "(size_t)(-1)" на 64-битной архитектуре представляется значением 0xFFFFFFFFFFFFFFFFui64, которое является максимальным значением для 64-битного типа size_t. Вернемся к ошибке с INVALID_RESULT. Использование константы 0xFFFFFFFFu приводит к невыполнению условия "len == (size_t)(-1)" в 64-битной программе. Наилучшее решение заключается в изменении кода так, чтобы специальных маркерных значений не требовалось. Если по какой-то причине Вы не можете от них отказаться или считаете нецелесообразным существенные правки кода, то просто используйте честное значение -1. #define INVALID_RESULT (size_t(-1)) ... Приведем еще один пример связанный с использованием 0xFFFFFFFF. Код взят из реального приложения для трёхмерного моделирования: hFileMapping = CreateFileMapping ( (HANDLE) 0xFFFFFFFF, NULL, PAGE_READWRITE, (DWORD) 0, (DWORD) (szBufIm), (LPCTSTR) &FileShareNameMap[0]); Как вы уже правильно догадались, 0xFFFFFFFF здесь также приведет к ошибке на 64-битной системе. Первый аргумент функции CreateFileMapping может иметь значение INVALID_HANDLE_VALUE, объявленное следующим образом: #define INVALID_HANDLE_VALUE ((HANDLE)(LONG_PTR)-1)
  • 4. В результате INVALID_HANDLE_VALUE действительно совпадает в 32-битной системе со значением 0xFFFFFFFF. А вот в 64-битной системе в функцию CreateFileMapping будет передано значение 0x00000000FFFFFFFF, в результате чего система посчитает аргумент некорректным и вернет код ошибки. Причина в том, что значение 0xFFFFFFFF имеет БЕЗЗНАКОВЫЙ тип (unsigned int). Значение 0xFFFFFFFF не помещается в тип int и поэтому является типом unsigned. Это тонкий момент, на который следует обратить внимание при переходе на 64-битные системы. Поясним его на примере: void foo(void *ptr) { cout << ptr << endl; } int _tmain(int, _TCHAR *[]) { cout << "-1tt"; foo((void *)-1); cout << "0xFFFFFFFFt"; foo((void *)0xFFFFFFFF); } Результат работы 32-битного варианта программы: -1 FFFFFFFF 0xFFFFFFFF FFFFFFFF Результат работы 64-битного варианта программы: -1 FFFFFFFFFFFFFFFF 0xFFFFFFFF 00000000FFFFFFFF Диагностика Статический анализатор PVS-Studio предупреждает о наличии в коде магических констант, имеющих наибольшую опасность при создании 64-битного приложения. Для этого используются диагностические сообщения V112 и V118. Учтите, анализатор сознательно не предупреждает о потенциальной ошибке, если магическая константа определена через макрос. Пример: #define MB_YESNO 0x00000004L MessageBox("Are you sure ?", "Question", MB_YESNO); Если совсем кратко, то причина такого поведения - защита от огромного количества ложных срабатываний. При этом считается, что если программист задает константу через макрос, то он
  • 5. делает это специально, чтобы подчеркнуть ее безопасность. Подробнее с данным вопросом можно познакомиться в записи блога на нашем сайте "Магические константы и функция malloc()". Авторы курса: Андрей Карпов (karpov@viva64.com), Евгений Рыжков (evg@viva64.com). Правообладателем курса "Уроки разработки 64-битных приложений на языке Си/Си++" является ООО "Системы программной верификации". Компания занимается разработкой программного обеспечения в области анализа исходного кода программ. Сайт компании: http://www.viva64.com. Контактная информация: e-mail: support@viva64.com, 300027, г. Тула, а/я 1800.