最短的崩潰程序(C語言版)

jopen 11年前發布 | 16K 次閱讀 C語言

  想寫個崩潰的C語言小程序,看起來是個奇怪的主意,不過在我曾經教過的一門實驗課上,這是作業之一!實際上,這是一件非常有教學意義的事情。

  通常學生們要么嘗試反向引用一個非法地址,要么就是除 0. 除 0 會引發 SIGFPE 信號(浮點異常)。這里有一個小例子程序,使用除零方法來使之崩潰:

int main ()
{
    return 1/0;
}

  我們也可以刪掉 return 關鍵字,但是當我這么做的時候 gcc 不會為這些語句生成可執行代碼,即便優化選項被 disable 掉了。我們還可以通過把上面的語句改成賦值語句,使上面的代碼改變一些特征:

i;int main ()
{
    i=1/0;
}

  注意我聲明了一個沒有類型的i。這樣的代碼在 C89 標準里是有效的,因為所有的聲明都有隱形的缺省類型 int。在 C99 和其他一些C標準里這是一個錯誤。假定我們寫的是 C89 代碼,那么我們甚至可以使用隱形 int 來聲明 main 函數:

i;
main ()
{
    i=1/0;
}

  那是相當短的代碼了 — 如果我們不把用于縮進的空格計算進來,只有 16 個字符。然而,我們還可以做得更好!

  當C程序在編譯的時候,編譯器會產生一個或更多對象文件,文件里有對于用到的庫和全程對象(函數和變量)的符號索引。然后這些對象文件會被進行鏈接,這時符號索引被地址所代替,就產生了一個可執行文件。

  編譯器在一個對象文件里提供了一個調用 main 函數的入口點。調用 main 函數意味著我們試圖執行在存儲在 main 函數鏈接的位置所對應地址里的指令。

  有趣的是,鏈接器對于不同對象的類型是沒有概念的,它只知道它們的地址。所以,如果我們用一個常規的全程變量替換 main 函數,編譯器會高興地 build 對象文件,因為它不關心對象 main 的類型是什么;鏈接器也會高興地鏈接它,因為它只關心 main 函數對應的地址。

  所以,考慮這個C程序:

int main=0;

  這個程序會編譯成一個可執行文件,它會試圖調用地址0,而 0 并不是我們能夠訪問的地址,這樣我們會得到 SIGSEGV 信號(分段錯誤)。

  更正:我前面關于這個程序崩潰的原因分析是錯的。這個程序會試圖按函數方式去執行 main,而這樣不會奏效,因為編譯器把它放到了不可執行的數據段。所以變量 main 初始化為什么值都無所謂了。(感謝 Zack 的糾正)

  現在我們已經非常接近最小的崩潰的C程序了。我們可以利用這個技巧,配合隱形 int 類型,來把它進一步縮短。

main=0;

  還有,C里的全局變量都會隱形地初始化為0,所以上面的代碼就等同于:

main;

  好了,現在我們得到了最短的崩潰的C程序!

  補充:

  Hacker News 用戶 femto 指出,編譯和鏈接一個空文件也是可能的。我沒有發布這個是因為 gcc 會拒絕編譯和鏈接這樣的程序,它會要求分開編譯和鏈接的過程。

  另外,要是我們再學究一點,我應該指出我這里的“全局”變量意思是說“靜態”變量。

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!