惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

爱范儿
爱范儿
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
G
GRAHAM CLULEY
www.infosecurity-magazine.com
www.infosecurity-magazine.com
V2EX - 技术
V2EX - 技术
The Last Watchdog
The Last Watchdog
S
Secure Thoughts
Webroot Blog
Webroot Blog
PCI Perspectives
PCI Perspectives
L
LINUX DO - 最新话题
Hacker News: Ask HN
Hacker News: Ask HN
N
News and Events Feed by Topic
H
Heimdal Security Blog
H
Help Net Security
T
The Blog of Author Tim Ferriss
P
Proofpoint News Feed
The GitHub Blog
The GitHub Blog
Jina AI
Jina AI
Recent Commits to openclaw:main
Recent Commits to openclaw:main
F
Full Disclosure
小众软件
小众软件
S
Securelist
罗磊的独立博客
NISL@THU
NISL@THU
D
Darknet – Hacking Tools, Hacker News & Cyber Security
C
Cisco Blogs
云风的 BLOG
云风的 BLOG
C
CERT Recently Published Vulnerability Notes
Cisco Talos Blog
Cisco Talos Blog
Know Your Adversary
Know Your Adversary
S
Schneier on Security
D
DataBreaches.Net
M
MIT News - Artificial intelligence
V
Vulnerabilities – Threatpost
N
News and Events Feed by Topic
有赞技术团队
有赞技术团队
F
Fortinet All Blogs
T
Tenable Blog
The Register - Security
The Register - Security
C
Check Point Blog
AWS News Blog
AWS News Blog
Cloudbric
Cloudbric
C
CXSECURITY Database RSS Feed - CXSecurity.com
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
C
Cyber Attacks, Cyber Crime and Cyber Security
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Google Online Security Blog
Google Online Security Blog
博客园 - 叶小钗
Hacker News - Newest:
Hacker News - Newest: "LLM"
博客园 - 司徒正美

Louis C Deng's Blog

CS231n Lecture Note: Generative Models CS231n Lecture Note: Self-Supervised Learning CS231n Lecture Note: Large Scale Distributed Training 自動微分 | DIY 實現自己的 PyTorch From RNNs to Transformers CS231n Lecture Note VII: Recurrent Neural Networks Uncovering Batch & Layer Normalization CS231n Lecture Note VI: CNN Architectures and Training CS231n Lecture Note V: Convolution Neural Networks Basics Demystifying Softmax Loss: A Step-by-Step Derivation for Linear Classifiers Backpropagation: A Vector Calculus Perspective CS231n Lecture Note IV: Neural Networks and Backpropagation CS231n Lecture Note III: Optimization CS231n Lecture Note II: Linear Classifiers CS231n Lecture Note I: Image Classification CSAPP Cache Lab II: Optimizing Matrix Transposition CSAPP Cache Lab I: Let's simulate a cache memory! CS188 Search Lecture Notes III CS188 Search Lecture Notes II How to Use TouchID for Sudo Commands on macOS CS188 Search Lecture Notes I RECAP2025: 留白 CSAPP Bomb Lab 解析 x64 暫存器速查表 CSAPP Data Lab 解析 矩陣的 Modified Gram Schmidt 方法 聊一聊位掩碼(Bit Mask) 快速排序 幾種劃分方法討論 等待 記夢(DeepSeek 輔助創作) 午夜飛行 橋樑 黎明 或 2012 RECAP2024: 水檻臥聽雨 太陽、潮落 RECAP2023: 泡沫 題解 P1622 釋放囚犯 題解 P5888 傳球遊戲 殘陽似火 再會 飢餓藝術家 卡夫卡 Python 中的 zip() 和 enumerate() 泡沫 “救救孩子……”——談魯迅和《狂人日記》 想念 淺灘 蟬 · 夏 微風 觀星 浮塵 復活 【摘錄 | 轉載】普魯斯特 《追憶似水年華》第一卷 《在斯萬家那邊》(一) Time - Pink Floyd - The Dark Side of the Moon 【轉載】靜夜思變調 高樓 幻夢 冰 RECAP2022: 流星雨 清夜 割點 Tarjan 演算法 P3147 USACO16OPEN 262144 P 題解 P3354 Riv 河流 題解 馬拉車演算法 夜雨 層霧 從愚人節玩笑到真的玩笑(bushi): 淺談 lsnotes I made my own Hexo theme 題解 紀念品分組 題解 導彈攔截 如何高效使用搜尋引擎 用 GitHub Actions 格式化 C/C++ 程式碼 四季的天空 洛谷 7 月月賽 Div.2 總結 題解 最近公共祖先 (LCA) 用簡單的物理方法證明牛頓萊布尼茨公式 簡評榮耀手環6 海上生明月,天涯共此時。 我為什麼重新拿出了 iPod Swift 中的 SharedPreferance —— UserDefaults 凝視那一輪明月 用 GitHub Actions 部署 Hexo 部落格 遲來的日誌 - WWDC 2020 獎學金 vcpkg - 方便的 C/C++ 庫管理器 vimrc 配置指南 NextCloud - DIY NAS 解決方案 sudo shutdown -r now sudo shutdown -r now
整數溢位與未定義行為
Louis C Deng · 2025-10-14 · via Louis C Deng's Blog

在做 CSAPP Data Lab 的時候,關於整數溢位,遇到一些問題。

題幹

1
2
3
4
5
6
7
8
9
10
11








int isTmax(int x) {
  return 2;
}

題目要求,僅僅使用運算子 ! ~ & ^ | + 來判斷一個數是否是最大的二的補碼(int 範圍內),即 0x7fffffff。如果是,輸出 1;否則,輸出 0。

思路

由於我們不能使用移位操作(很多人會直接 1<<31 - 1),可以考慮整數溢位的特殊性質。

具體地,我們有 0x7fffffff + 1 = 0x80000000,符號改變。

0x80000000 + 0x80000000 = 0

我們可以得到 x = 0x7fffffff 滿足 x + 1 + x + 1 = 0

而對於其他數字,假設 y = x + k 其中 k 非零,則有 y + 1 + y + 1 = 2*k

此時,我們發現,對於 y=-1 也有 y + 1 + y + 1 = 0,需要排除掉

其他情況下,非零數轉換為 bool 型別自動變為 1

我們不難寫出以下程式碼:

1
2
3
4
5
int isTmax(int x) {
  int p1 = x+1;
  int p2 = p1 + p1;
  return !(p2) & !!(p1);
}

發現問題

這段程式碼在我本地(macOS,Apple clang version 17.0.0 (clang-1700.3.19.1), Target: arm64-apple-darwin25.0.0) 上執行,使用命令 clang main.c 是沒有任何問題的。

但是,檢查到 CSAPP 提供的 Makefile,有

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24



CC = gcc
CFLAGS = -O -Wall
LIBS = -lm

all: btest fshow ishow

btest: btest.c bits.c decl.c tests.c btest.h bits.h
$(CC) $(CFLAGS) $(LIBS) -o btest bits.c btest.c decl.c tests.c

fshow: fshow.c
$(CC) $(CFLAGS) -o fshow fshow.c

ishow: ishow.c
$(CC) $(CFLAGS) -o ishow ishow.c


btestexplicit:
$(CC) $(CFLAGS) $(LIBS) -o btest bits.c btest.c decl.c tests.c

clean:
rm -f *.o btest fshow ishow *~

注意到,編譯器使用了 -O flag,即 O1 最佳化。

此時執行這段程式碼,對於 0x7fffffff 輸出 0,懷疑可能是編譯器最佳化時,假設未定義行為(整數溢位)不會發生,將 !p2 最佳化。p1 + p1 的形式過於簡單。

未定義行為

未定義行為(UB),根據 cppreference 的定義:

1
undefined behavior - There are no restrictions on the behavior of the program.

有符號整數溢位是一種常見的未定義行為。

Because correct C++ programs are free of undefined behavior, compilers may produce unexpected results when a program that actually has UB is compiled with optimization enabled.

也就是說,編譯器最佳化會對未定義行為產生意料之外的結果

cppreference 給出了一個整數溢位的例子:

1
2
3
4
5
int foo(int x)
{
return x + 1 > x;
}

編譯之後卻變成了

1
2
3
foo(int):
mov eax, 1
ret

意思是,不管怎麼樣都輸出 1

觀察出錯程式碼

我們透過 gcc -S 輸出編譯後的彙編程式碼

1
2
3
4
5
6
7
_Z6isTmaxi:
.LFB2:
.cfi_startproc
endbr64
movl $0, %eax
ret
.cfi_endproc

我們看到,編譯器直接把這個函式返回值改成了 0,不管輸入什麼,與我們的錯誤原因推斷是相同的。

修改

我們可以嘗試構造一個更復雜的、不易被簡單規則匹配的表示式,躲過 O1 級別的最佳化。

核心思路不變,仍然是利用 Tmax + 1 = Tmin 這個特性。我們來觀察一下 TmaxTmin 在二進位制下的關係:

  • Tmax = 0x7fffffff = 0111...1111
  • Tmin = 0x80000000 = 1000...0000

一個非常有趣的性質是 Tmax + Tmin = -1 (0xffffffff)。

1
2
3
4
  0111 1111 ... 1111  (Tmax)
+ 1000 0000 ... 0000 (Tmin)
-------------------------
1111 1111 ... 1111 (-1)

基於這個觀察,我們可以設計一個新的檢查方案:如果一個數 xTmax,那麼 x + (x+1) 的結果就應該是 -1。取反後 ~(-1) 則為 0

我們可以寫出如下的修改版程式碼:

1
2
3
4
5
int isTmax(int x) {
int map = x + 1;
int res = ~(map + x);
return !res & (!!map);
}

這段程式碼的邏輯是:

  1. 計算 map = x + 1。對於 x = Tmax,這裡同樣會發生有符號溢位,map 變為 Tmin這依然是未定義行為(UB)
  2. 計算 res = ~(map + x)。如果 xTmax,這一步就是 ~(Tmin + Tmax),結果為 ~(-1),即 0
  3. return !res & (!!map)!res!0,即 1!!map 部分和之前的版本一樣,是為了排除 x = -1 的情況(此時 map 為 0, !!map 為 0,最終返回 0)。

這段程式碼在 -O 最佳化下可能會得到正確的結果。

為什麼這個“可能”有效?

我們必須清醒地認識到,新版本的程式碼本質上沒有解決未定義行為的問題,它只是“僥倖”地繞過了當前編譯器版本的特定最佳化策略。

  • 程式碼模式的複雜性p1 + p1 ((x+1)+(x+1)) 是一個非常簡單直白的模式,最佳化器很容易建立一個“如果 p1 非零,則 p1+p1 結果也非零”的最佳化規則。而 ~((x+1)+x) 混合了加法和位運算,模式更復雜,可能沒有觸發編譯器中已有的、基於UB的最佳化捷徑。
  • 最佳化的機會主義:編譯器最佳化並不是要窮盡所有的數學可能,而是應用一系列已知的高效模式。我們的新程式碼恰好不在這些常見模式的“黑名單”上。

所以,這個修改版只是一個更具迷惑性的“偽裝”。它在特定環境下能工作,但其行為是不被C語言標準所保證的,在不同的編譯器或未來的GCC版本下,它隨時可能失效。

結論:如何正確面對未定義行為

透過 isTmax 這個小小的函式,我們可以一窺C語言中未定義行為的危險性以及現代編譯器最佳化的強大。作為開發者,我們應該得到以下啟示:

  1. 不要依賴未定義行為:永遠不要編寫依賴於UB的程式碼,即使它“在你的機器上看起來能跑”。程式碼的健壯性來源於對語言標準的嚴格遵守,而非僥倖。
  2. 相信編譯器,但要驗證:編譯器非常聰明,它會嚴格按照語言規範進行最佳化。當你發現最佳化後的程式碼行為不符合你的“直覺”時,首先應該懷疑自己的程式碼是否觸碰了UB的紅線。
  3. 善用工具
    • 始終開啟編譯器警告 (-Wall -Wextra) 並將警告視為錯誤 (-Werror),這能幫你發現許多潛在問題。
    • 使用執行時檢測工具,如GCC/Clang的 UndefinedBehaviorSanitizer (UBSan)。只需在編譯時加上 -fsanitize=undefined,它就能在程式執行時精確地捕獲有符號整數溢位等UB,是除錯這類問題的神器。

對於CSAPP Data Lab這道題來說,它的目的正是為了讓我們在“規則的鐐銬”下舞蹈,從而深刻理解整數表示、運算和編譯器行為。而我們在實際工程中,最安全、最清晰的寫法永遠是第一選擇。