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

C Primer Plus 核心筆記:整數溢出環模型與格式化輸出完整解析

     編輯:關於C語言

前言

整數溢出與格式化輸出是 C 語言最基礎也最易踩坑的知識點。本文結合《C Primer Plus》第 2 章內容,通過實測代碼深度解析 有符號/無符號溢出的環模型本質、printf 格式符與實際類型不匹配時的位模式解讀、八/十六進制輸出細節,建立正確的數據存儲與輸出心智模型。

一、整數溢出:環模型的數學本質

1.1 實測代碼與輸出


int i = 2147483647;      // INT_MAX (2^31-1)
unsigned int j = 4294967295; // UINT_MAX (2^32-1)

printf("%d %d %d\n", i, i+1, i+2); // 2147483647 -2147483648 -2147483647
printf("%u %u %u\n", i, i+1, i+2); // 2147483647 2147483648 2147483649
printf("%d %d %d\n", j, j+1, j+2); // -1 0 1
printf("%u %u %u\n", j, j+1, j+2); // 4294967295 0 1

1.2 環模型統一解釋

計算機中整數以 固定寬度二進制補碼 存儲,本質是 模 2N 的循環群:

  • 無符號數:範圍 [0, 2^N-1],溢出等價於 mod 2^N,行為**確定**。
  • 有符號數:範圍 [-2^(N-1), 2^(N-1)-1],同樣按補碼環運算,但 C 標準規定**有符號溢出 = 未定義行為 (UB)**,編譯器可假設不發生並優化。實際運行中多數編譯器仍按補碼環執行。

1.3 逐行解析

表達式 補碼位模式 (32位) %d 解讀 (有符號) %u 解讀 (無符號)
i (INT_MAX) 0x7FFFFFFF (0111...111) 2147483647 2147483647
i+1 0x80000000 (1000...000) -2147483648 (INT_MIN) 2147483648
i+2 0x80000001 (1000...001) -2147483647 2147483649
j (UINT_MAX) 0xFFFFFFFF (1111...111) -1 4294967295
j+1 0x00000000 (溢出歸零) 0 0
j+2 0x00000001 1 1

核心結論:**內存中的位模式不變,只是解讀方式 (格式符) 不同**。%d 將最高位視為符號位,%u 視為數值位。這就是「同一份二進制,既可以是有符號數,也可以是無符號數」的真諦。

二、格式化輸出:類型不匹配時的「位模式直讀」

2.1 實測代碼


unsigned int un = 3000000000; // 0xB2D05E00
short end = 200;              // 0x00C8
long big = 65537;             // 0x00010001
long long verybig = 12345678908642; // 0x0000B3E6 5D5E5E2 (64位)

printf("%u, %d\n", un, un);       // 3000000000, -1294967296
printf("%hd, %d\n", end, end);    // 200, 200
printf("%ld, %hd\n", big, big);   // 65537, 1
printf("%lld, %ld\n", verybig, verybig); // 12345678908642, 1942899938

2.2 解析規律

  1. 參數傳遞:printf 是可變參函數,**默認參數提升**:float→double,char/short→int,其餘保持。調用方不傳類型信息。
  2. 格式符決定解讀:printf 根據格式符從棧/寄存器取指定字節數,按格式符語義解讀位模式。
  3. 截取/擴展:
    • %hd 取低 16 位 (short);%d 取 32 位 (int);%ld 取 32/64 位 (long);%lld 取 64 位。
    • 若實際參數位寬 > 格式符期望,高位被截斷;若 < 期望,**高位內容不定** (棧垃圾)。

2.3 逐行詳解

  • un = 3000000000 (0xB2D05E00):%u 正確顯示;%d 視為有符號,最高位 1 → 負數,補碼值 -1294967296。
  • end = 200 (0x00C8):提升為 int 0x000000C8,%hd 取低 16 位 = 200,%d 取 32 位 = 200。
  • big = 65537 (0x00010001):%ld 正確顯示 65537;%hd 只取低 16 位 0x0001 = 1。
  • verybig:64 位值。%lld 正確顯示;%ld (32 位 long) 只取低 32 位 0x5D5E5E2 = 1942899938。

三、八進制與十六進制輸出:# 標誌的作用


int x = 100; // 0x64, 0144
printf("%d %o %x\n", x, x, x);    // 100 144 64
printf("%d %#o %#x\n", x, x, x);  // 100 0144 0x64
  • %o 八進制,%x 十六進制小寫,%X 大寫。
  • # 標誌:八進制加前綴 0,十六進制加 0x/0X。

四、總結:三大核心心智模型

  1. 環模型:定寬整數 = 模 2N 環,溢出即繞圈。無符號安全,有符號 UB 但通常同樣繞圈。
  2. 位模式不變,解讀方式變:同一組比特,用 %d 看是有符號,用 %u 看是無符號,用 %x 看是十六進制。**printf 不知類型,只信格式符**。
  3. 參數提升 + 截取/擴展:可變參函數中類型信息丟失,格式符決定取多少字節、怎麼解讀。類型不匹配 = 截取低位或讀取垃圾高位。

五、工程避坑清單

  • ✅ 計算前預判溢出:if (a > INT_MAX - b) overflow。
  • ✅ 格式符嚴格匹配類型:long long 用 %lld,size_t 用 %zu (C99),intptr_t 用 %" PRIdPTR 。
  • ✅ 打印指針用 %p (配合 void*),不可用 %x/%lu。
  • ✅ 開啟編譯器警告:-Wformat -Wformat-security -Wconversion,捕獲 90% 不匹配 Bug。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved