首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

兩個經典奇葩 C 程序深度分析:遞歸 main 與數組下標交換律

     編輯:關於C語言

程序一:遞歸調用 main 實現計數器


#include 
#include 

void main(int i) {
    printf("%d ", i);
    (&main + (&exit - &main) * (i/1000))(i+1);
}

運行結果

1 2 3 ... 999 1000
退出碼: 233 (1001 % 256)

核心機制解析

  1. main 參數:`void main(int i)` 非標準但被 GCC 支持。程序啟動時 argc=1 傳給 i
  2. 函數指針運算:`&main` 取 main 地址,`&exit` 取 exit 地址
  3. 關鍵表達式:`(&main + (&exit - &main) * (i/1000))`
    • i < 1000 時:i/1000 = 0 → 地址 = &main → 遞歸調用 main(i+1)
    • i = 1000 時:i/1000 = 1 → 地址 = &exit → 調用 exit(1001)
  4. 地址空間假設:依賴 main 與 exit 在代碼段相對固定偏移,屬於未定義行為,僅供技術探討

標準合規版本


#include 
#include 

void recursive_main(int i) {
    printf("%d ", i);
    if (i < 1000) recursive_main(i+1);
    else exit(i+1);
}

int main() {
    recursive_main(1);
    return 0;
}

程序二:數組下標交換律 `a[5] == 5[a]`


#include 

void main() {
    int a[] = {1,2,3,4,5,6};
    printf("a[5]=%-5d 5[a]=%-5d", a[5], 5[a]);
}

輸出

a[5]=6     5[a]=6

原理深度解析

C 標準定義:E1[E2] 等價於 *(E1 + E2)

  • `a[5]` → `*(a + 5)` → 取地址 a 偏移 5 个 int 的內容
  • `5[a]` → `*(5 + a)` → 取地址 5 偏移 a 个 int 的內容
  • 指針加法滿足交換律:`a + 5 == 5 + a`

內存模型視角

假設 a 起始地址 0x1000,int 4 字節:
a[0] @ 0x1000
a[1] @ 0x1004
...
a[5] @ 0x1014

a + 5 = 0x1000 + 5*4 = 0x1014
5 + a = 5*4 + 0x1000 = 0x1014  (編譯器將整數 5 視為偏移量)
*(a+5) = *(5+a) = 6

歷史典故

此題曾為 MIT C 語言考試經典題目,考察對 數組本質 = 指針 + 偏移 的根本理解。許多初學者誤以為 `a[5]` 是特殊語法,實則是語法糖。

現代編譯器行為

代碼GCC -O2 生成彙編說明
a[5]mov eax, DWORD PTR [rdi+20]基址 + 偏移 20(=5*4)
5[a]mov eax, DWORD PTR [rdi+20]完全相同機器碼

總結與警示

⚠️ 警告:程序一利用了非標準 main 簽名、函數指針算術、代碼段佈局假設,屬於未定義行為,僅供學習理解編譯器底層機制,絕不可用於生產代碼。
✅ 知識點:數組下標交換律揭示了 C 語言數組設計的本質——指針算術的語法糖。這也是為什麼 C 數組下標從 0 開始:`a[0]` = `*(a+0)` = `*a` 直接取首元素。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved