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

C語言堆棧佈局揭秘:為什麼2+2能等於5?指針算術與內存對齊深度解析

     編輯:關於C語言

看似荒謬的「2+2=5」:C語言指針算術與棧佈局的隱藏邏輯

網上流傳一段代碼聲稱能打印「2+2=5」,實則利用字符指針算術與棧上變量佈局的巧合。本文深度剖析其原理,並探討編譯器差異(GCC vs Clang)對棧佈局的影響。

原始「魔術」代碼

int main() {
    char __func_version__[] = "5";  // 目標字符串
    char b[] = "2", a = 2;          // a=2, b指向"2"
    printf("%d + %s = %s\n", a, b, a + b); // a+b 做指針加法!
    return 0;
}

a+b中a隱式轉為int,b退化為char*,結果是b + 2,即指向b後第3個字節。若棧佈局恰好使該地址落在__func_version__的'5'上,便打印出「5」。

實測:GCC -O3 下的棧佈局

#include <stdio.h>
int main() {
    char __func_version__[] = "5";
    char b[] = "2", a = 2;
    printf("%p %p %p\n", __func_version__, b, &a);
    printf("%d + %s = %s\n", a, b, a + b);
    return 0;
}

輸出:

0x7fff504fa920 0x7fff504fa930 0x7fff504fa910
2 + 2 = OP?

__func_version__地址低於b,b+2指向更高地址,無法觸及目標,打印亂碼。

解決方案一:結構體強制佈局 (#pragma pack(1))

#pragma pack(1)
typedef struct { char *b; char a; char *version; } foo;
void print_5_by_struct() {
    foo f = {"2", 2, "5"};
    printf("%d + %s = %s\n", f.a, f.b, f.a + f.b); // 穩定輸出 2 + 2 = 5
}

#pragma pack(1)消除填充字節,確保成員緊湊排列,f.b + 2精確落在f.version上。

解決方案二:改用 Clang 編譯器

同樣代碼在 clang -O3 下:

0x7fff57925936 0x7fff57925934 0x7fff57925933
2 + 2 = 5

Clang 將變量按聲明順序由高地址向低地址分配,恰好滿足條件。

💡 核心知識點總結

  1. 指針算術:ptr + n 移動 n * sizeof(*ptr) 字節。
  2. 棧佈局不確定性:編譯器、優化等級、ABI 均影響局部變量相對地址,不可依賴。
  3. 類型懲罰:將 int 當指針偏移屬於未定義行為,生產代碼嚴禁此類技巧。
  4. 調試技巧:打印%p觀察地址,理解內存模型是C程序員必修課。
學C光看書不夠,必須「學以致用」,動手實驗內存佈局、編譯器行為,才能真正掌握語言本質。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved