コンピュータプログラミングにおいて、マジックナンバーまたはマジック定数とは、ソースコード内の数値リテラルで、その意味が読者には不明瞭なものです。この用語はプログラミング以外でも、追加の知識がなければ意味が明らかでない数値を指すのに使用されます(例えば、一部のファイル形式は、ファイルに埋め込まれたマジックナンバーによって識別されます)。また、普遍的に一意な識別子など、特定の概念と比較的一意に関連付けられている数値も、マジックナンバーとして分類される場合があります。
ソースコードでマジックナンバーを使用することはアンチパターンとみなされ、 1960年代のCOBOL、FORTRAN、PL/1のマニュアルにまで遡るプログラミングの最も古いルールの1つを破ることになります。 [ 1 ]
例えば、税込み価格を計算する以下のコードでは、1.05はマジックナンバーです。なぜなら、その値は売上税率 5% を分かりにくい方法でエンコードしているからです。
税込み価格 = 1.05 * 価格
コード内でマジックナンバーを使用すると、開発者がその数値を選択した意図が不明瞭になり、[ 2 ]微妙なエラーが発生する可能性が高まり、将来的にプログラムを適応および拡張することがより困難になります。[ 3 ]たとえば、のすべての桁が3.14159265358979323846正しく入力されているかどうか、またはこの円周率の定数を精度が低下してもプログラムの機能に影響を与えずに切り捨てることができるかどうかを判断するのは困難です。重要なマジックナンバーをすべて名前付き定数3.14159(説明変数とも呼ばれます)に置き換えると、プログラムが読みやすく、理解しやすく、保守しやすくなります。[ 4 ]
上記の例は、分かりやすい名前の変数を追加することで改善できます。
税金 = 0.05 税込み価格 = (1.0 + 税) * 価格
適切な名前は、元の作者ではない保守担当者や、一定期間が経過した後の元の作者にとっても理解しやすいコードにつながります。[ 5 ]情報量の少ない定数名の例としては がありますがint SIXTEEN = 16、 の方int NUMBER_OF_BITS = 16がより有用かもしれません。
非数値データもマジックナンバーと同様の魔法的な性質を持ち、そのためマジックナンバーと同様の問題を抱える可能性があります。[ 1 ]したがって、リテラルを直接使用するよりも宣言しconst string testUserName = "John"て使用する方testUserNameが良いかもしれません"John"。
例えば、標準的なトランプの束を表す配列の値をランダムにシャッフルする必要がある場合、この擬似コードはフィッシャー・イェーツ・シャッフル・アルゴリズムを使用してその処理を実行します。
i を1から52まで繰り返す j := i + randomInt(53 - i) - 1 a.swapEntries(i, j)
ここでa、は配列オブジェクトであり、関数は1からxrandomInt(x)までのランダムな整数を選択し、配列のi番目とj番目の要素を交換します。前の例では、とはマジックナンバーであり、互いに明確な関連性はありません。次のように記述する方がより良いプログラミングスタイルと考えられます。swapEntries(i, j)5253
int deckSize:= 52 for i from 1 to deckSize j := i + randomInt(deckSize + 1 - i) - 1 a.swapEntries(i, j)
これはいくつかの理由から好ましい。
deckSizeは、たった1行の変更で済みます。dekSize」の代わりに「 deckSize」と入力すると、未宣言であるというコンパイラの警告が表示されますdekSize。deckSizeその手順のパラメータにするだけで十分ですが、最初の例ではいくつかの変更が必要になります。function shuffle ( int deckSize) for i from 1 to deckSize j := i + randomInt(deckSize + 1 - i) - 1 a.swapEntries(i, j)
デメリットは以下のとおりです。
deckSize + 1。実行時に式を処理する場合、「53」という値よりも処理速度が遅くなる可能性があります。とはいえ、最新のコンパイラのほとんどは、コンパイル時に定数畳み込みやループ最適化などの手法を用いて加算を解決するため、コード内でマジックナンバーを使用する場合と比べて、速度低下は通常ほとんど、あるいは全くありません。特に、デバッグのコストや、説明のないコードを理解しようとするのに必要な時間を考慮すると、計算コストはごくわずかです。数値リテラルに特別な意味がない場合、その使用は魔法とはみなされませんが、何が特別な意味を持つかは主観的なものです。魔法とはみなされないことが多いリテラルの例としては、以下のようなものがあります。
for(inti=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などの式における指数NULLnullptrフォーマットインジケータは、Unixバージョン7の初期のソースコードで初めて使用されました。
Unix は、メモリ保護機能を持たない初期のDEC PDP-11 /20の 1 つに移植されました。そのため、初期のバージョンの Unix は再配置可能なメモリ参照モデルを使用していました。[ 6 ]第 6 版以前のUnixバージョンでは、実行可能ファイルをメモリに読み込み、プログラムの最初の低位メモリ アドレス(相対アドレス0) にジャンプしていました。ページング対応バージョンの Unix の開発に伴い、実行可能イメージのコンポーネントを記述するためのヘッダーが作成されました。また、ヘッダーをスキップしてプログラムを開始するための分岐命令がヘッダーの最初のワードとして挿入されました。このようにして、プログラムは古い再配置可能なメモリ参照 (通常) モードまたはページング モードのどちらでも実行できるようになりました。より多くの実行可能フォーマットが開発されるにつれて、分岐オフセットをインクリメントすることで新しい定数が追加されました。[ 7 ]
Unix プログラム ローダーの第6 版のソース コードでは、exec() 関数はファイルシステムから実行可能 (バイナリ) イメージを読み込んでいました。ファイルの最初の 8バイトは、プログラム (テキスト) と初期化済み (グローバル) データ領域のサイズを含むヘッダーでした。また、ヘッダーの最初の 16 ビット ワードは 2 つの定数と比較され、実行可能イメージに再配置可能なメモリ参照(通常)、新しく実装されたページングされた読み取り専用実行可能イメージ、または分離された命令とデータのページングされたイメージが含まれているかどうかを判断しました。 [ 8 ]ヘッダー定数の二重の役割については言及されていませんでしたが、定数の最上位バイトは実際にはPDP-11 分岐命令の操作コード(8進数000407 または16 進数0107) でした。プログラム カウンタに 7 を加えると、この定数が実行されると、Unix exec() サービスが実行可能イメージの 8 バイトのヘッダーを分岐してプログラムを開始することがわかりました。
Unixの第6版と第7版ではページングコードが採用されていたため、ヘッダー定数の二重の役割は隠蔽されていました。つまり、exec()サービスは実行可能ファイルのヘッダー(メタ)データをカーネル空間のバッファに読み込みましたが、実行可能イメージはユーザー空間に読み込んだため、定数の分岐機能は使用されませんでした。マジックナンバーの生成はUnixリンカとローダで実装されており、マジックナンバーの分岐は第6版と第7版に付属していた一連のスタンドアロン診断プログラムでも使用されていたと考えられます。したがって、ヘッダー定数は錯覚を生み出し、マジックナンバーの条件を満たしていました。
バージョン7 Unixでは、ヘッダー定数は直接テストされず、ux_mag [ 9 ]というラベルの変数に割り当てられ、その後マジックナンバーと呼ばれるようになりました。おそらくその一意性のため、マジックナンバーという用語は実行可能フォーマットタイプを意味するようになり、次にファイルシステムタイプを意味するように拡張され、さらにあらゆる種類のファイルを意味するように拡張されました。
マジックナンバーは、多くのオペレーティングシステムのプログラムでよく見られます。マジックナンバーは、厳密に型付けされたデータを実装するものであり、プログラム実行時にデータ型を読み取る制御プログラムへのインバンドシグナリングの一種です。多くのファイルには、含まれるデータを識別するためのこのような定数が含まれています。ファイル内のこのような定数を検出することは、多くのファイル形式を区別するシンプルかつ効果的な方法であり、実行時にさらなる情報を得ることができます。
CA FE BA BECA FE D0 0D47 49 46 38 39 6147 49 46 38 37 61FF D8FF D94A 46 49 46 0045 78 69 66 0089 50 4E 47 0D 0A 1A 0A4D 54 68 6423 217F実行ファイルは、バイトの後に「ELF」()が続く形で始まります。7F 45 4C 4625 2125 50 44 464D 5A5A 4D19 54 01 1901 19 5455 AA4A 6F 79 2149 49 2A 004D 4D 00 2AFE FFFF FEEF BB BF42 43D0 CF 11 E050 4B 03 0437 7A BC AF 27 1CUnixのユーティリティプログラムはfile、ファイルからマジックナンバーを読み取って解釈することができ、情報を解析するために使用されるファイルは「マジック」と呼ばれます。WindowsのユーティリティTrIDも同様の目的で使用されます。
2A52 46 42FF 53 4D 42\xFFSMB05リクエストの開始時に (Microsoft DCE/RPC バージョン 5 を表す)で始まり、その直後にマイナー バージョンの場合は00または が続きます01。UDP ベースの MSRPC リクエストでは、最初のバイトは常に です04。4D 45 4F 574D 41 52 4219、その直後にバイト位置 1 に「BitTorrent プロトコル」というフレーズが続きます。E3は eDonkey クライアント、C5は eMule、 はD4圧縮された eMule を表します。D9 B4 BE F9DA B5 BF FA80クライアントハローに対するSSLv3サーバーの応答はで始まります16(ただし、これは異なる場合があります)。63 82 53 63PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n258EAFA5-E914-47DA-95CA-C5AB0DC85B11。マジックナンバーは、 DOS、Windows、NetWareなど、多くのオペレーティングシステムのAPI関数やインターフェースでよく使われています。
00 0012 3455 AABA BEEB AB0E DCグローバル一意識別子(GUID) を記憶しやすいように作成または変更することは可能ですが、ほぼ一意の識別子としての強度を損なうため、強く推奨されません。 [ 15 ] [ 16 ] GUID と UUID を生成するための仕様は非常に複雑であり、適切に実装すれば事実上一意になる理由となっています。[ 17 ]
Microsoft Office製品の Microsoft Windows 製品 ID 番号は、末尾に0000-0000-0000000FF1CE「OFFICE」が付く場合があります。たとえば90160000-008C-0000-0000-0000000FF1CE、「Office 16 Click-to-Run 拡張コンポーネント」の製品 ID などです。
Java は . で始まるいくつかの GUID を使用しますCAFEEFAC。[ 18 ]
GPTパーティショニング方式のGUIDパーティションテーブルでは、 BIOSブートパーティションはGUID定義に従わない特別なGUID 21686148-6449-6E6F-744E-656564454649[ 19 ]を使用します。代わりに、文字列のASCIIHah!IdontNeedEFIコードを部分的にリトルエンディアン順で使用して形成されます。[ 20 ]
マジックデバッグ値は、メモリの割り当てまたは解放時にメモリに書き込まれる特定の値であり、後でそれらが破損したかどうかを判別したり、初期化されていないメモリから取得した値が使用されていることを明確に示したりするために使用されます。メモリは通常16進数で表示されるため、覚えやすい繰り返し値や16進数特有の値がよく使われます。バイトアドレッシングを持たないプロセッサがポインタとして使用しようとしたときにエラーが発生するように、数値的に奇数の値が好まれる場合があります(ポインタは偶数アドレスに配置される必要があります)。値は、想定されるアドレス(プログラムコード、静的データ、ヒープデータ、スタックなど)から離れた場所に選択する必要があります。同様に、特定のアーキテクチャの命令セットにおいて有効なコードにならないように選択することもできます。
32ビット整数がこの特定の値を取る可能性は非常に低いものの、あり得ないことではないため、デバッガやメモリダンプにこのような数値が現れた場合は、バッファオーバーフローや未初期化変数などのエラーを示している可能性が最も高いです。
よく知られた例としては、以下のようなものがあります。
これらのほとんどは32ビット長で、これはほとんどの32ビットアーキテクチャのコンピュータのワードサイズと同じです。
マイクロソフトのテクノロジーにおいてこれらの価値観が広く浸透しているのは偶然ではありません。これらの価値観については、スティーブ・マグワイア著『Writing Solid Code』(マイクロソフト・プレス刊)で詳しく解説されています。彼はこれらの価値観について、次のような様々な基準を挙げています。
これらの用語は、実質的に空のメモリ領域を示すためによく使われていたため、「消えた、中止された、メモリから消去された」という意味のフレーズで使用されるようになりました。例:「Your program is DEADBEEF」。