複製鏈接
請複製以下鏈接發送給好友

魔數

鎖定
魔數這個詞在不同領域代表不同的含義。在計算機領域,魔數有兩個含義,一指用來判斷文件類型的魔數;二指程序代碼中的魔數,也稱魔法值。
中文名
魔數
外文名
magic number
別    名
魔法數

魔數定義

  1. 所謂魔數和魔字符串就是指在代碼中出現但沒有解釋的數字常量字符串,又稱魔法值。如果在某個程序中你使用了魔數,那麼在幾個月或幾年後你將很可能不知道它的含義是什麼。
  2. 大多數情況下,我們都是通過擴展名來識別一個文件的類型的,比如我們看到一個.txt類型的文件我們就知道他是一個純文本文件。但是,擴展名是可以修改的,當一個文件的擴展名被修改過,識別一個文件的類型就用到了我們提到的“魔數”。很多類型的文件,其起始的幾個字節的內容是固定的(或是有意填充,或是本就如此)這幾個字節的內容也被稱為魔數,因為根據這幾個字節的內容就可以確定文件類型。有了這些魔術數字,我們就可以很方便的區別不同的文件。

魔數實例解釋

代碼中的魔數
譬如一個很簡單的根據職位計算薪水的方法
public int getSalary(String title, int grade)
{
  if ("Programmer".equals(title))
    return grade * 500 + 700;
  else 
    if ("Tester".equals(title))
    	return grade * 500 + 800;
  	else
      if ("Analyst".equals(title))
      	return grade * 800 + 1000;}
在這個方法裏面,"Programmer","Tester"和"Analyst"是所謂的魔字符串(Magic String),而500, 700,800和1000就是所謂的魔數(Magic Number)了。 咋一看,代碼這樣寫也沒有什麼問題,但是,仔細思考一下就會發現,如果這種隨手捻來的字符串和數字散佈於程序當中,隨處可見的話,是會有很多弊病的。我們先來分析三個魔字符串。雖然三個Magic String的意義很明顯,並不影響到代碼的可讀性,但是這樣卻增加了出錯的概率,並且忽略了具體的語義環境。我們很容易就會想到,像"Programmer"這個單詞散佈在多個方法中,一個大小寫的筆誤就會產生bug。同時,"Programmer"在計算薪水的方法中代表着職位,但是在統計公司訂閲的雜誌的方法中,也許就要代表一本雜誌的名稱了。然而這種語義環境是無法通過一個單純的"Programmer"就能體現出來的。
Magic Number
而Magic Number的問題就更大了,首先是影響了代碼的可讀性,誰會知道500和800是薪水基數,700是補貼.而且更糟糕的是,如果薪水基數發生改變的時候,那麼就得找人把這些500,700,800的數字找出來一個一個地update,那可是一件夠鬱悶的事情了。
如果我們擁有一個常量定義的interface,代碼就會變漂亮起來了:
public int getSalary(String title, int grade) {
if (Constants.TITLE_PROGRAMMER.equals(title))
return grade * Constants.BASE_SALARY_LOW + Constants.ALLOWANCE_LOW;
else if (Constants.TITLE_TESTER.equals(title))
return grade * Constants.BASE_SALARY_LOW + Constants.ALLOWANCE_MEDIUM;
else if (Constants.TITLE_ANALYST.equals(title))
return grade * Constants.BASE_SALARY_HIGH + Constants.ALLOWANCE_HIGH;
}
從以上的分析,在一個Project裏面,避免使用魔數(Magic Number)和魔字符串(Magic String)是相當必要的。通過定義的常量去access特定的字符串和數字也已經是軟件開發的standard。那麼是不是所有的數字和字符串都應該定義成常量呢。或許有朋友會認為所有的數字和字符串都應該定義成常量,但是我覺得,每個字符串確實是應該定義成常量的,但是對於數字而言,如果數字本身的語義沒有得到延伸,那麼就不應該定義成常量。譬如數組的index就不應該定義成變量。 像這樣的代碼:
String building = address[Constants.ONE];
// 在Constants這個interface中,ONE的定義為 final int ONE = 1;
你一定會覺得這樣的代碼就是畫蛇添足, 因為ONE就是1,它沒有其他特別的含義,不像上面代碼中的500和700。而且如果真的要這樣定義的話,出現了有上百個元素的數組的時候,那麼你就得定義上百個沒有任何意義的常量了。這樣會大大降低代碼質量,浪費內存空間。
在程序中除了0,1,2這幾個有特殊用處的數字,其它的都要聲明為常量。總之,任何策略的使用,還是一個度最重要。
判斷文件類型的魔數
很多類型的文件,其起始的幾個字節的內容是固定的(或是有意填充,或是本就如此)。因此這幾個字節的內容也被稱為魔數 (magic number),因為根據這幾個字節的內容就可以確定文件類型。
例如
  • FreeBSD 上 ELF 文件的 magic number 就是文件的前四個字節依次為"7f 45 4c 46",對應的ascii字符串即 "^?ELF"。
  • tar 文件的 magic number 是從第257個字節起為 "ustar"。
  • PE文件中,在DOS-根之後是一個32位的簽名以及魔數0x00004550 (IMAGE_NT_SIGNATURE) 意為“NT簽名”,也就是PE簽名;十六進制數45和50分別代表ASCII碼字母P和E,它使任何PE文件都是一個有效的MS-DOS可執行文件。
  • Java的.class文件,開頭的4個字節0xCAFEBABE,這是Java初代開發小組最喜歡的一種咖啡Peet's Coffee的愛稱。
Unix 命令 "file" 應該就是利用這個原理工作的。Linux操作系統下沒有文件擴展名,判斷文件類型憑藉的就是文件的內容,也就是魔數。

魔數核學定義

魔數是穩定核素"幻數"的另稱。