網路上流傳一段代碼聲稱能在 C 語言印出「2+2=5」:
int main() {
char __func_version__[] = "5";
char b[] = "2", a = 2;
printf("%d + %s = %s\n", a, b, a + b);
return 0;
}
實際編譯執行(GCC -O3)並非印出 5,而是不可列印字元。原理是嘗試利用堆疊變數佈局,讓 a+b 正好指向 __func_version__ 的 "5",但編譯器優化、堆疊對齊、變數排序皆不受控,極不可靠。
加入位址列印觀察 GCC 4.8.2 (-O3) 佈局:
char __func_version__[] = "5";
char b[] = "2", a = 2;
printf("%p %p %p\n", __func_version__, b, &a);
// 輸出:0x7fff504fa920 0x7fff504fa930 0x7fff504fa910
__func_version__ 位址 b 更小(棧向下增長),a+b 只會往高位址偏移,永遠無法觸及前者。整數迴繞也無法精確落在 "5" 位置。
利用 #pragma pack(1) 封裝結構體控制記憶體佈局:
#pragma pack(1)
typedef struct __foo {
char *b;
char a;
char *__func_version__;
} foo;
void print_5_by_struct(void) {
foo foo_v = {"2", (char)2, "5"};
printf("%p %p\n", foo_v.__func_version__, foo_v.b);
printf("%d + %s = %s\n", foo_v.a, foo_v.b, foo_v.a + foo_v.b);
}
成功印出「2+2=5」。結構體成員順序與緊湊排列確保 a+b 落在 __func_version__ 位址。
同樣代碼在 Clang 5.1 (LLVM 3.4) -O3 下:
clang -std=c99 -Wall -O3 -g0 -o 5 5.c // 輸出位址:0x7fff57925936 0x7fff57925934 0x7fff57925933 // 直接印出:2 + 2 = 5
Clang 將變數按宣告順序緊湊排列(__func_version__ 最高、b 中、a 最低),a+b 剛好指向 "5"。展示編譯器對區域變數佈局策略差異巨大。