В компьютерном программировании магическим числом является любое из следующих чисел:
Термин «магическое число» или «магическая константа» относится к антипаттерну использования чисел непосредственно в исходном коде. Это было названо нарушением одного из старейших правил программирования, восходящего к руководствам по COBOL , FORTRAN и PL/1 1960-х годов. [1] Использование безымянных магических чисел в коде скрывает намерения разработчиков при выборе этого числа, [2] увеличивает вероятность тонких ошибок (например, правильна ли каждая цифра в 3,14159265358979323846 и равно ли это 3,14159?) и усложняет задачу. для того, чтобы программа была адаптирована и расширена в будущем. [3] Замена всех значащих магических чисел именованными константами (также называемыми объяснительными переменными) упрощает чтение, понимание и поддержку программ. [4]
Имена, выбранные так, чтобы они были значимыми в контексте программы, могут привести к тому, что код будет легче понять сопровождающему, который не является первоначальным автором (или даже исходному автору через определенный период времени). [5] Примером константы с неинформативным именем является int SIXTEEN = 16
, хотя int NUMBER_OF_BITS = 16
она более информативна.
Описанные выше проблемы, связанные с магическими «числами», не ограничиваются числовыми типами, этот термин также применяется к другим типам данных, где объявление именованной константы было бы более гибким и коммуникативным. [1] Таким образом, объявление const string testUserName = "John"
лучше, чем несколько вхождений «магического значения» "John"
в набор тестов .
Например, если требуется случайным образом перетасовать значения в массиве, представляющем стандартную колоду игральных карт , этот псевдокод выполняет эту работу, используя алгоритм перемешивания Фишера-Йейтса :
для меня от 1 до 52 j := я + randomInt(53 - i) - 1 а.swapEntries(я, j)
где a
— объект массива, функция randomInt(x)
выбирает случайное целое число от 1 до x включительно и swapEntries(i, j)
меняет местами i -ю и j -ю записи в массиве. В предыдущем примере 52
— магическое число. Считается, что лучшим стилем программирования будет написать следующее:
int DeckSize:= 52 для i от 1 до DeckSize j := i + randomInt(deckSize + 1 - i) - 1 а.swapEntries(я, j)
Это предпочтительнее по нескольким причинам:
deckSize
во втором примере будет простым изменением в одну строку.dekSize
» вместо « » приведет к необъявленному deckSize
предупреждению компилятора .dekSize
deckSize
в параметр этой процедуры, тогда как первый пример потребует нескольких изменений.функция shuffle ( inteckSize ) для i от 1 до DeckSize j := i + randomInt(deckSize + 1 - i) - 1 а.swapEntries(я, j)
Недостатками являются:
deckSize + 1
Обработка выражения во время выполнения может быть медленнее, чем значение «53», хотя большинство современных компиляторов и интерпретаторов заметят, что deckSize
оно объявлено как константа, и предварительно вычислят значение 53 в скомпилированном коде. Даже если это невозможно, оптимизация цикла переместит сложение так, чтобы оно выполнялось до цикла. Поэтому обычно нет (или незначительное) снижение скорости по сравнению с использованием магических чисел в коде. В частности, стоимость отладки и время, необходимое для понимания необъяснимого кода, должны быть сопоставлены с крошечными затратами на вычисления.В некоторых контекстах использование безымянных числовых констант является общепринятым (и, возможно, «не магическим»). Хотя такое принятие является субъективным и часто зависит от индивидуальных привычек программирования, типичными примерами являются следующие:
for (int i = 0; i < max; i += 1)
isEven = (x % 2 == 0)
, где %
оператор по модулюcircumference = 2 * Math.PI * radius
, [1] или для вычисления дискриминанта квадратного уравнения какd = b^2 − 4*a*c
(f(x) ** 2 + f(y) ** 2) ** 0.5
forКонстанты 1 и 0 иногда используются для представления логических значений True и False в языках программирования без логического типа, например в более старых версиях C. Большинство современных языков программирования предоставляют примитивный типboolean
или , поэтому использование 0 и 1 не рекомендуется. Это может еще больше сбить с толку, поскольку 0 иногда означает программный успех (когда -1 означает неудачу), а в других случаях — неудачу (когда 1 означает успех).bool
В C и C++ 0 представляет собой нулевой указатель . Как и в случае с логическими значениями, стандартная библиотека C включает определение макроса NULL
, использование которого приветствуется. Другие языки предоставляют определенное значение null
или nil
, и в этом случае не следует использовать альтернативу. Константа типизированного указателя nullptr
была введена в C++11.
Индикаторы формата впервые использовались в исходном коде Unix ранней версии 7 . [ нужна цитата ]
Unix был портирован на один из первых DEC PDP-11 /20, не имевший защиты памяти . Поэтому ранние версии Unix использовали эталонную модель перемещаемой памяти . [6] Версии Unix до шестой редакции считывают исполняемый файл в память и переходят к первому младшему адресу памяти программы, относительному нулевому адресу . С развитием страничных версий Unix был создан заголовок для описания компонентов исполняемого образа . Кроме того, в качестве первого слова заголовка была вставлена инструкция перехода , позволяющая пропустить заголовок и запустить программу. Таким образом, программу можно было запустить в старом режиме ссылки на перемещаемую память (обычном) или в страничном режиме. По мере разработки большего количества исполняемых форматов добавлялись новые константы путем увеличения смещения ветвления . [7]
В исходном коде загрузчика программ Unix шестой редакции функция exec() считывает исполняемый ( двоичный ) образ из файловой системы. Первые 8 байт файла представляли собой заголовок , содержащий размеры программной (текстовой) и инициализированных (глобальных) областей данных. Кроме того, первое 16-битное слово заголовка сравнивалось с двумя константами , чтобы определить, содержит ли исполняемый образ ссылки на перемещаемую память (обычные), недавно реализованный страничный исполняемый образ, доступный только для чтения, или разделенное страничное изображение инструкции и данных. [8] Не было упоминания о двойной роли константы заголовка, но старший байт константы фактически был кодом операции для инструкции ветвления PDP-11 ( восьмеричное 000407 или шестнадцатеричное 0107). Добавление семи к счетчику программ показало, что если эта константа будет выполнена , она разветвит службу Unix exec() по восьмибайтовому заголовку исполняемого образа и запустит программу.
Поскольку в шестом и седьмом изданиях Unix использовался страничный код, двойная роль константы заголовка была скрыта. То есть служба exec() считывает данные заголовка исполняемого файла ( мета ) в буфер пространства ядра , но считывает исполняемый образ в пространство пользователя , тем самым не используя функцию ветвления константы. Создание магических чисел было реализовано в компоновщике и загрузчике Unix , а ветвление магических чисел, вероятно, все еще использовалось в наборе автономных диагностических программ , поставляемых с шестой и седьмой редакциями. Таким образом, константа заголовка действительно создавала иллюзию и соответствовала критериям магии .
В седьмой версии Unix константа заголовка не проверялась напрямую, а присваивалась переменной с меткой ux_mag [9] и впоследствии называлась магическим числом . Вероятно, из-за своей уникальности термин «магическое число» стал обозначать тип исполняемого формата, затем расширился до типа файловой системы и снова расширился до обозначения любого типа файла.
Магические числа распространены в программах во многих операционных системах. Магические числа реализуют строго типизированные данные и являются формой внутриполосной передачи сигналов управляющей программе, которая считывает типы данных во время выполнения программы. Многие файлы имеют такие константы, которые идентифицируют содержащиеся в них данные. Обнаружение таких констант в файлах является простым и эффективным способом различения многих форматов файлов и может предоставить дополнительную информацию во время выполнения .
CAFEBABE
. При сжатии с помощью Pack200 байты изменяются на CAFED00D
.47
49
46
38
39
61
) или «GIF87a» ( 47
49
46
38
37
61
).FF
D8
начинаются с расширения FF
D9
. Файлы JPEG/ JFIF содержат строку с нулевым завершением «JFIF» ( 4A
46
49
46
00
). Файлы JPEG/ Exif содержат строку «Exif» ( ), оканчивающуюся нулем45
78
69
66
00
, за которой следуют дополнительные метаданные о файле.89
50
4E
47
0D
0A
1A
0A
). Эта подпись содержит различные символы новой строки , позволяющие обнаруживать необоснованные автоматические преобразования новой строки, такие как передача файла по FTP с режимом передачи ASCII вместо двоичного режима. [10]4D
54
68
64
23
21
), за которым следует путь к интерпретатору , если интерпретатор, скорее всего, будет отличаться от того, из которого был вызван сценарий.7F
за которым следует «ELF» ( 7F
45
4C
46
).25
21
).25
50
44
46
).4D
5A
), инициалов разработчика формата файла Марка Збиковски . Определение допускает использование необычного «ZM» ( 5A
4D
), а также для dosZMXP, EXE-файла, отличного от PE. [11]19
54
01
19
или 01
19
54
в зависимости от версии; оба представляют день рождения автора, Маршалла Кирка МакКьюсика .55
AA
4A
6F
79
21
) в качестве префикса.49
49
2A
00
. «ММ» относится к компании Motorola, которая использует обратный порядок байтов, поэтому магическое число — 4D
4D
00
2A
.FE
FF
для прямого и FF
FE
обратного порядка байтов). А в Microsoft Windows текстовые файлы UTF-8 часто начинаются с кодировки UTF-8 того же символа, EF
BB
BF
.42
43
).D0
CF
11
E0
, что визуально напоминает слово «DOCFILE0».50
4B
03
04
), где «PK» — это инициалы Фила Каца , автора утилиты сжатия DOS PKZIP .37
7A
BC
AF
27
1C
).Служебная программа Unix file
может читать и интерпретировать магические числа из файлов, а файл, который используется для анализа информации, называется магическим . Утилита Windows TrID имеет аналогичную цель.
2A
.52
46
42
для «Удаленного буфера кадров»), за которым следует номер версии протокола клиента.FF
53
4D
42
» или "\xFFSMB"
в начале запроса SMB.05
начала запроса (представляющего Microsoft DCE/RPC версии 5), за которым сразу следует символ 00
или 01
для младшей версии. В запросах MSRPC на основе UDP первый байт всегда равен 04
.4D
45
4F
57
). Расширения отладки (используемые для перехвата канала DCOM) начинаются с последовательности байтов «MARB» ( 4D
41
52
42
).19
, представляющее длину заголовка, за которым сразу следует фраза «Протокол BitTorrent» в позиции байта 1.E3
представляет клиент eDonkey, C5
представляет eMule и D4
представляет собой сжатый eMule.0xD9B4BEF9
, которая указывает на основную сеть, а константа 0xDAB5BFFA
указывает на тестовую сеть.80
а ответ сервера SSLv3 на приветствие клиента начинается с 16
(хотя это может отличаться).0x63
0x82
0x53
0x63
в начале раздела параметров пакета. Это значение включено во все типы пакетов DHCP.0x505249202a20485454502f322e300d0a0d0a534d0d0a0d0a
' или " PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n
". Предисловие предназначено для того, чтобы избежать обработки кадров серверами и посредниками, поддерживающими более ранние версии HTTP, кроме 2.0.Магические числа часто встречаются в функциях и интерфейсах API во многих операционных системах , включая DOS , Windows и NetWare :
0000
и 1234
решают, должна ли система подсчитывать память или нет при перезагрузке, тем самым выполняя холодную или теплую загрузку. Эти значения также используются менеджерами памяти EMM386, перехватывающими запросы на загрузку. [12] BIOS также использует магические значения 55 AA
, чтобы определить, является ли диск загрузочным. [13]Это список ограничений типов хранения данных: [15]
Можно создавать или изменять глобальные уникальные идентификаторы (GUID), чтобы они запоминались, но это крайне не рекомендуется, поскольку это ставит под угрозу их силу как почти уникальных идентификаторов. [16] [17] Спецификации для генерации GUID и UUID довольно сложны, что приводит к тому, что они практически уникальны, если их правильно реализовать. . [ нужна цитата ]
Идентификаторы продуктов Microsoft Windows для продуктов Microsoft Office иногда заканчиваются на 0000-0000-0000000FF1CE
(«OFFICE»), например { 90160000-008C-0000-0000-0000000FF1CE
}, идентификатор продукта для «Компонента расширения Office 16 «нажми и работай».
Java использует несколько идентификаторов GUID, начиная с CAFEEFAC
. [18]
В таблице разделов GUID схемы разделов GPT загрузочные разделы BIOS используют специальный GUID { 21686148-6449-6E6F-744E-656564454649
} [19] , который не соответствует определению GUID; вместо этого он формируется с использованием кодов ASCII для строки « Hah!IdontNeedEFI
» частично в прямом порядке байтов . [20]
Магические значения отладки — это особые значения, записываемые в память во время выделения или освобождения, чтобы позже можно было определить, были ли они повреждены, а также сделать очевидным, когда используются значения, взятые из неинициализированной памяти. Память обычно рассматривается в шестнадцатеричном формате, поэтому часто встречаются запоминающиеся повторяющиеся или шестнадцатеричные значения. Предпочтительнее использовать числовые нечетные значения, чтобы процессоры без байтовой адресации выдавали ошибку при попытке использовать их в качестве указателей (которые должны приходиться на четные адреса). Значения следует выбирать вдали от вероятных адресов (код программы, статические данные, данные кучи или стек). Аналогично, они могут быть выбраны так, чтобы они не были допустимыми кодами в наборе команд для данной архитектуры.
Поскольку очень маловероятно, хотя и возможно, что 32-битное целое число приняло бы именно это значение, появление такого числа в отладчике или дампе памяти , скорее всего, указывает на такую ошибку, как переполнение буфера или неинициализированная переменная .
Известные и распространенные примеры включают:
Большинство из них имеют длину 32 бита — размер слова большинства компьютеров с 32-битной архитектурой.
Преобладание этих ценностей в технологиях Microsoft не случайно; они подробно обсуждаются в книге Стива Магуайра «Написание надежного кода» , изданной Microsoft Press . Он дает различные критерии этих ценностей, такие как:
Поскольку они часто использовались для обозначения областей памяти, которые были по существу пустыми, некоторые из этих терминов стали использоваться во фразах, означающих «ушло, прервано, сброшено из памяти»; например, «Ваша программа DEADBEEF». [ нужна цитата ]