2009-11-05 19 views
2

不言而喻,使用硬編碼,十六進制文字的指針是一個災難:究竟使用魔術調試值(如0xDEADBEEF)作爲文字有什麼危險?

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

然而,究竟是什麼在使用十六進制文字作爲變量的危險?

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

如果這些值確實是「危險的」,因爲它們use in various debugging scenarios,那麼這意味着即使我不使用這些文字,任何程序,在運行期間發生在這些值的一個絆倒可能會崩潰。

任何關心解釋使用十六進制文字的真正危險?


編輯:只是爲了澄清,我不是指在源代碼中一般使用常量。我特別討論了調試場景問題,它們可能會使用十六進制值,具體示例爲0xDEADBEEF

+0

您的問題標題與您的問題非常相符。你能否澄清你的標題? – 2009-11-05 16:24:04

回答

2

我認爲今天早些時候在IP地址格式化問題中引起的擔憂與通常使用十六進制文字無關,而是與0xDEADBEEF的具體使用有關。至少,這是我閱讀它的方式。

特別是使用0xDEADBEEF時有一個問題,雖然在我看來這是一個小問題。問題在於許多調試器和運行時系統已經將這個特定值指定爲標記值來指示未分配的堆,堆棧上的壞指針等。

我不記得我的頭頂哪些調試和運行時系統使用這個特定的值,但我看到它多年來多次使用這種方式。如果您正在這些環境之一中進行調試,則代碼中0xDEADBEEF常量的存在與未分配RAM中的值無關,因此最好不要使用RAM轉儲,最壞的情況下會收到警告來自調試器。

無論如何,這就是我認爲最初的評論者告訴你「在各種調試場景中使用」的不好之處。

3

十六進制文字與十進制文字一樣沒有區別。值的任何特殊含義都是由於特定程序的上下文引起的。

+1

與問題無關,但是......恭喜您擁有1024個信譽。 ;) – Powerlord 2009-11-05 15:31:26

+0

雖然沒有持續很長時間... – 2009-11-05 18:12:32

11

使用十六進制文字比任何其他類型的文字都沒有更多的危險。

如果您的調試會話最終將數據作爲代碼執行,而您並不打算這樣做,那麼您仍然處於痛苦的世界。

當然,這裏有正常的「魔法值」與「常見的」代碼氣味/清潔度問題,但這並不是我想說的那種危險。

+1

喬恩,這可能已經被問到/在別處回答,但你爲什麼改名爲託尼小馬? – 2009-11-05 15:59:15

+1

@Graham:http://meta.stackexchange.com/questions/28192/has-jon-skeet-been-hacked – 2009-11-05 16:35:01

1

我沒有看到使用它作爲值的任何問題。它畢竟只是一個數字。

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"); 

應該聲明一個「常量」 - 並命名 - 只需一次。

單元測試中的「結果」與常量不同,需要小心解釋它來自哪裏。

+2

我不是指使用命名常量的問題。我正在談論使用這些常量的調試場景。 – 2009-11-05 15:56:19

2

沒有理由不把0xdeadbeef分配給變量。

但是,有些程序員試圖分配小數點3735928559或八進制33653337357,或者最糟糕的是:二進制11011110101011011011111011101111

1

在正確的上下文中使用指針的硬編碼十六進制值(如第一個示例)沒有任何危險。特別是,當進行非常低級別的硬件開發時,這是您訪問內存映射寄存器的方式。 (例如,最好給他們一個#define的名字)。但是在應用程序級別,你不需要做這樣的任務。

2

Big Endian or Little Endian?

將常量分配給具有不同大小成員的數組或結構時,會有一個危險: endian - 編譯器或機器(包括JVM和CLR)的性能會影響字節的排序。

當然,這個問題對於非常量值也是如此。

這是一個被公認爲人爲的例子。 緩衝區[0]最後一行後的值是多少?

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

memcpy(buffer, (void*)TEST, sizeof(TEST)); 
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 
} 

重複上述所有你需要在你的代碼中使用i 7000個實例。

現在,爲什麼上面比這更糟?

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

至少,我可以當場幾個關鍵問題:

  1. 拼寫:我是一個可怕的打字員。在代碼審查中你會發現0xDAEDBEEF有多容易?還是0xDEADBEFF?另一方面,我知道我的編譯會立即在isIProperlyInitialised()處插入(這裏插入強制性的z辯論)。
  2. 意義的暴露。不是試圖在代碼中隱藏標誌,而是有意創建了一個代碼的其餘部分可以看到的方法。
  3. 耦合機會。指針或引用連接到鬆散定義的緩存是完全可能的。初始化檢查可能被重載,首先檢查該值是否在緩存中,然後嘗試將其帶回緩存中,如果全部失敗,則返回false。

簡而言之,編寫您真正需要的代碼就像創建一個神祕的魔法值一樣簡單。未來的代碼維護者(很有可能是你)會感謝你。

+0

再次,不是我想要的...... – 2009-11-05 19:52:07

1

我使用CAFEBABE 我以前沒有看到它被任何調試器使用過。

相關問題