Ноль
- 1 year ago
- 0
- 0
Понятие « Магическое число » в программировании имеет три значения:
Магическое число
, или
сигнатура
, —
целочисленная
или текстовая
константа
, используемая для однозначной
идентификации
или
данных
. Такое число само по себе не несёт никакого смысла и может вызвать недоумение, встретившись в
коде программы
без соответствующего контекста или
комментария
, при этом попытка изменить его на другое, даже близкое по значению, может привести к абсолютно непредсказуемым последствиям. По этой причине подобные числа были
иронично
названы
магическими
. В настоящее время это название прочно закрепилось как
термин
. Например, любой откомпилированный класс языка
Java
начинается с
шестнадцатеричного
«магического числа»
0xCAFEBABE
. Второй широко известный пример — любой исполняемый файл ОС Microsoft Windows с расширением .exe начинается с последовательности байт
0x4D5A
(что соответствует
ASCII
-символам
MZ
— инициалы
Марка Збиковски
, одного из создателей
MS-DOS
). Менее известным примером является неинициализированный указатель в Microsoft Visual C++ (начиная с 2005 версии Microsoft Visual Studio), который в режиме отладки имеет адрес
0xDEADBEEF
.
В UNIX-подобных операционных системах тип файла обычно определяется по сигнатуре файла, вне зависимости от расширения его названия. Для интерпретации сигнатуры файла в них предусматривается стандартная утилита
file
.
Также «магическими числами» называют плохую практику программирования, когда в исходном тексте встречается числовое значение и неочевиден его смысл. Например, такой фрагмент, написанный на Java , будет плохим:
drawSprite(53, 320, 240);
Тому, кто не писал программу, трудно понять, что такое 53, 320 или 240. Но если этот код переписать, всё становится на свои места.
final int SCREEN_WIDTH = 640; final int SCREEN_HEIGHT = 480; final int SCREEN_X_CENTER = SCREEN_WIDTH / 2; final int SCREEN_Y_CENTER = SCREEN_HEIGHT / 2; final int SPRITE_CROSSHAIR = 53; ... drawSprite(SPRITE_CROSSHAIR, SCREEN_X_CENTER, SCREEN_Y_CENTER);
Теперь понятно: этот код выводит в центр экрана спрайт — перекрестие прицела. В большинстве языков программирования все значения, используемые для таких констант, будут вычислены ещё на этапе компиляции и подставлены в места использования значений ( свёртка констант ). Поэтому такое изменение исходного текста не ухудшает быстродействие программы.
Кроме того, магические числа — потенциальный источник ошибок в программе:
Иногда магические числа вредят
кроссплатформенности
кода
. Дело в том, что в
Си
в 32- и 64-битных ОС гарантируется размер типов
char
,
short
и
long long
, в то время как размер
int
,
long
,
size_t
и
ptrdiff_t
может меняться (у первых двух — в зависимости от предпочтений разработчиков компилятора, у последних двух — в зависимости от разрядности целевой системы). В старом или неумело написанном коде могут встречаться «магические числа», означающие размер какого-либо типа — при переходе на машины с другой разрядностью они могут привести к трудноуловимым ошибкам.
Например:
const size_t NUMBER_OF_ELEMENTS = 10; long a[NUMBER_OF_ELEMENTS]; memset(a, 0, 10 * 4); // неправильно — подразумевается, что long равен 4 байтам, используется магическое число элементов memset(a, 0, NUMBER_OF_ELEMENTS * 4); // неправильно — подразумевается, что long равен 4 байтам memset(a, 0, NUMBER_OF_ELEMENTS * sizeof(long)); // не совсем правильно — дублирование имени типа (если изменится тип, то придется менять и здесь) memset(a, 0, NUMBER_OF_ELEMENTS * sizeof(a[0])); // правильно, оптимально для динамических массивов ненулевого размера memset(a, 0, sizeof(a)); // правильно, оптимально для статических массивов
Не все числа требуется переносить в константы. Например, код на Delphi :
for i:=0 to Count-1 do ...
Смысл чисел 0 и 1 понятен, и дальнейшего объяснения не требуется.