聊聊程式設計中的 “魔數”

程式設計師魚皮發表於2022-03-11

大家好,我是魚皮,無意間在網上看到了這麼一張圖:

剛看到這段程式碼時,我是比較吃驚的,作者竟然使用了一個英文單詞 deadbeef 來定義巨集常量!

我本來以為只是一位幽默的程式設計師的小玩笑,但後來查閱資料才知道,上圖的這段程式碼竟是 C++ 的 hash_map 原始碼!而作者使用這個特殊的英文單詞也是 “別有用心”。

deadbeef 的英文直譯是死牛肉,但在程式設計領域中,它卻有更深層的含義。給這個單詞加上 0x 、再轉換為大寫,就得到了一個典型的十六進位制數字:0xDEADBEEF。這個數字經常用來標識新分配但是還未初始化的記憶體;在嵌入式系統中,也常常用它來表示程式崩潰或者出現了死鎖,比如執行在 32 位 PowerPC 處理器上的 IBM RS/6000 系統、Mac OS 系統。

那我不禁感到好奇,為什麼選擇了這樣一個單詞,而不是 “FishPi” 之類的(開個玩笑,16 進位制最多到 F)。

到網上查了一會,得到的結論竟然是:沒什麼理由,它是一個 “魔數”!

所謂魔數,就是毫無理由、憑空出現、也不需要去解釋其含義的常量。就是這麼任性!

除了 deadbeef 外,我還百度到了很多魔數,比如:

  • 0xBAADF00D ("bad food" 爛飯) 被微軟的 LocalAlloc(LMEM_FIXED)使用,在使用除錯堆時指示未初始化的已分配堆記憶體
  • 0xDEADC0DE ("dead code" 死碼) 在 OpenWRT 韌體中用作標記,在靜態韌體的末尾表示要建立的 jffs2 檔案系統的開始
  • 0xDEAD10CC ("dead lock" 死鎖) 用於表示 iOS 系統的閃退報告

是不是感覺很神奇?也許這就是程式設計師的浪漫吧。

看到這裡,我忍不住了,也去寫了幾個魔數,大家來猜猜看是什麼意思:

redisLock.lease(86400);
if (fileSize > 1073741824) {
  ...doSomething
}
if (num > 2147483647) {
  printf("you lose");
}

這幾個值都是我們寫程式碼時經常用的,84600 = 3600 * 24 表示一天;1073741824 = 1024 * 1024 * 1024 表示 1 GB;而 2147483647 是 Java 等程式語言中 int 型別的最大值。

我把這些程式碼拿給我朋友一看,他嘲笑道:人家大佬寫的魔數叫魔數,而你寫的,只能叫爛程式碼。

的確,除非是上面那些大佬 / 前輩公認的、約定俗成的魔數外,我們在平時寫程式碼的時候,儘量不要使用魔數,它會嚴重影響程式碼的可讀性。我們可以通過定義常量來給這些魔數加上 “註釋”,比如:

int ONE_DAY = 86400;
int ONE_GB = 1073741824;
int MAX_INTEGER = 2147483647;

這樣就清晰很多了,也減少了我們輸入錯誤的風險。

除了上面提到的魔數外,我還在網上看到了一些有實際意義的魔數,比如現代 3D 遊戲之父約翰·卡馬克在雷神之錘中的魔數:

i = 0x5f3759df - ( i >> 1 );

完全不敢相信,上面這行程式碼竟然可以快速計算一個數字的平方根的倒數!

在網上一查,還有很多論文專門研究這個東西:

不得不感嘆程式設計的魅力、數學的魅力啊!什麼時候,我也能創造一個人盡皆知的魔數呢?

“喂,魚皮,別特麼做夢了,來搬磚!”

“來了來了,我再給你寫幾個魔數(爛程式碼)!”


最後,魚皮開了個程式設計學習圈子,裡面有幾千名學程式設計的小夥伴,我會在裡面直播帶大家做專案~ 指路:http://dogyupi.com

相關文章