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

В некачественном коде часто встречаются магические числовые константы, наличие которых опасно само по себе. При миграции кода на 64-битную платформу эти константы могут сделать код неработоспособным, если участвуют в операциях вычисления адреса, размера объектов или в битовых операциях.

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

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

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

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

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

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 alternative3) 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)#endifconst 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; // 0xFFFFFFFFi32ptrdiff_t b = a; // 0xFFFFFFFFFFFFFFFFi64size_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

Мой блог находят по следующим фразам

Данная статья "Урок 09. Паттерн 1. Магические числа" размещена на сайте Компьютерные сети и многоуровневая архитектура интернета (conlex.kz) в ознакомительных целях.

Уточнения, корректировки и обсуждения статьи "Урок 09. Паттерн 1. Магические числа" - под данным текстом, в комментариях.

Ответственность, за все изменения, внесённые в систему по советам данной статьи, Вы берёте на себя.

Копирование статьи "Урок 09. Паттерн 1. Магические числа", без указания ссылки на сайт первоисточника Компьютерные сети и многоуровневая архитектура интернета (conlex.kz), строго запрещено.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *