2

言うまでもなく、ハードコーディングされた 16 進リテラル ポインターを使用するのは大惨事です。

int *i = 0xDEADBEEF;
// god knows if that location is available

しかし、変数値として 16 進リテラルを使用する危険性は正確には何ですか?

int i = 0xDEADBEEF;
// what can go wrong?

これらの値がさまざまなデバッグ シナリオで使用されているために実際に「危険」である場合、これは、これらのリテラルを使用しなくても、実行時にこれらの値のいずれかに遭遇したプログラムがクラッシュする可能性があることを意味します。

16 進リテラルを使用することの本当の危険性を説明したい人はいますか?


編集:明確にするために、ソースコードでの定数の一般的な使用について言及していません。具体的には、16 進数値を使用する際に発生する可能性のあるデバッグ シナリオの問題について0xDEADBEEF、具体的な例を挙げて説明しています。

4

10 に答える 10

11

16 進リテラルを使用しても、他の種類のリテラルよりも危険はありません。

意図せずにデバッグ セッションがデータをコードとして実行することになった場合、とにかく苦痛の世界にいることになります。

もちろん、通常の「魔法の値」と「名前の付いた定数」のコードの匂い/清潔さの問題がありますが、それはあなたが話しているような危険ではありません.

于 2009-11-05T15:12:23.437 に答える
5

いくつかの例外を除いて、「一定」のものはありません。

私たちはそれらを「遅い変数」と呼ぶことを好みます。値の変化が非常に遅いため、変更するために再コンパイルする必要はありません。

ただし、アプリケーションまたはテスト スクリプト全体で 0x07 の多くのインスタンスが発生することは望ましくありません。各インスタンスには異なる意味があります。

各定数にラベルを付けて、それが何を意味するのかを完全に明確にしたいと考えています。

if( x == 7 )

上記の文の「7」はどういう意味ですか? と同じことですか

d = y / 7;

「7」と同じ意味ですか?

テスト ケースは、少し異なる問題です。数値リテラルの各インスタンスを広範かつ慎重に管理する必要はありません。代わりに、ドキュメントが必要です。

コードにほんの少しのヒントを含めることで、"7" の由来をある程度説明できます。

assertEquals( 7, someFunction(3,4), "Expected 7, see paragraph 7 of use case 7" );

「定数」は、1 回だけ記述し、名前を付ける必要があります。

単体テストの「結果」は定数と同じではなく、どこから来たのかを説明する際に少し注意が必要です。

于 2009-11-05T15:13:48.013 に答える
3

16 進リテラルは、1 のような 10 進リテラルと同じです。値の特別な意味は、特定のプログラムのコンテキストによるものです。

于 2009-11-05T15:12:14.497 に答える
2

今日の IP アドレス形式に関する質問で提起された懸念は、一般的な 16 進数リテラルの使用に関連したものではなく、0xDEADBEEF の特定の使用に関連したものだと思います。少なくとも、私はそう読んでいます。

特に 0xDEADBEEF の使用には懸念がありますが、私の意見では小さいものです。問題は、多くのデバッガーとランタイム システムが、未割り当てのヒープやスタック上の不良ポインターなどを示すマーカー値として、この特定の値を既に採用していることです。

どのデバッグ システムとランタイム システムがこの特定の値を使用しているのか、頭の中で思い出すことはできませんが、この値がこのように使用されているのを何年にもわたって何度か見てきました。これらの環境のいずれかでデバッグしている場合、コード内の 0xDEADBEEF 定数の存在は、割り当てられていない RAM などの値と区別がつかないため、せいぜい有用な RAM ダンプが得られず、最悪の場合、警告が表示されます。デバッガーから。

とにかく、元のコメンターが「さまざまなデバッグ シナリオで使用する」のが悪いと言ったのは、そういう意味だったと思います。

于 2009-11-05T15:27:53.623 に答える
2

0xdeadbeef変数に代入してはいけない理由はありません。

3735928559しかし、 decimal 、または octal 33653337357、または最悪の場合、 binaryを割り当てようとするプログラマーは悲惨11011110101011011011111011101111です。

于 2009-11-05T15:19:23.587 に答える
2

ビッグエンディアンかリトルエンディアンか?

危険の 1 つは、定数が異なるサイズのメンバーを持つ配列または構造体に割り当てられる場合です。コンパイラまたはマシン (JVM と CLR を含む)のエンディアンは、バイトの順序に影響します。

もちろん、この問題は非定数値にも当てはまります。

これは、確かに不自然な例です。最後の行の後のbuffer[0]の値は?

const int TEST[] = { 0x01BADA55,  0xDEADBEEF };
char buffer[BUFSZ];

memcpy( buffer, (void*)TEST, sizeof(TEST));
于 2009-11-05T15:50:03.680 に答える
1

適切なコンテキストで (最初の例のように) ポインターにハードコードされた 16 進値を使用しても危険はありません。特に、非常に低レベルのハードウェア開発を行う場合、これはメモリ マップド レジスタにアクセスする方法です。(ただし、たとえば #define を使用して名前を付けるのが最善です。) しかし、アプリケーション レベルでは、そのような割り当てを行う必要はありません。

于 2009-11-05T15:22:55.410 に答える
1

私は CAFEBABE を使用していますが、これまでデバッガーで使用されたことはありません。

于 2010-02-03T02:00:40.170 に答える
1

値として使用しても問題はないと思います。結局はただの数字です。

于 2009-11-05T15:13:31.967 に答える
0
int *i = 0xDEADBEEF;
// god knows if that location is available

int i = 0xDEADBEEF;
// what can go wrong?

私が目にする危険はどちらの場合も同じです: 直接のコンテキストを持たないフラグ値を作成しました。iいずれの場合も、潜在的に重要なフラグ値が関連付けられていることを 100、1000、または 10000 行知らせてくれるものは何もありません。あなたが植え付けたのは地雷のバグであり、考えられるすべての用途でそれをチェックすることを覚えていなければ、恐ろしいデバッグの問題に直面する可能性があります. のすべての使用は、次のiようになります。

if (i != 0xDEADBEEF) { // Curse the original designer to oblivion
    // Actual useful work goes here
}

コードで使用する必要がある 7000 個のインスタンスすべてに対して上記を繰り返しiます。

さて、なぜ上記はこれよりも悪いのでしょうか?

if (isIProperlyInitialized()) { // Which could just be a boolean
    // Actual useful work goes here
}

少なくとも、いくつかの重大な問題を見つけることができます。

  1. スペル: 私はひどいタイピストです。コード レビューで 0xDAEDBEEF を見つけるのはどれくらい簡単ですか? それとも0xDEADBEFF?一方、私のコンパイルは isIProperlyInitialized() ですぐにバーフすることを知っています (必須の szの議論をここに挿入してください)。
  2. 意味の暴露。コード内でフラグを隠そうとするのではなく、残りのコードから見えるメソッドを意図的に作成しました。
  3. カップリングの機会。ポインターまたは参照が大まかに定義されたキャッシュに接続されている可能性は十分にあります。初期化チェックをオーバーロードして、最初に値がキャッシュにあるかどうかをチェックし、次にそれをキャッシュに戻そうと試み、すべて失敗した場合は false を返すことができます。

要するに、不思議な魔法の値を作成するのと同じくらい、本当に必要なコードを書くのは簡単です。将来のコード管理者 (あなたになる可能性が高い) は、あなたに感謝するでしょう。

于 2009-11-05T17:27:36.813 に答える