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

C 語言 char 型別溢位陷阱:strlen 回傳 255 的真相

     編輯:關於C

現象重現

#include <stdio.h>
#include <string.h>

int main() {
    char arr[1000];
    for (int i = 0; i < 1000; i++) {
        arr[i] = -1 - i;
    }
    printf("%d\n", strlen(arr));  // 輸出 255
    return 0;
}

直覺預期可能是 1000 或 0,實際輸出卻是 255。這背後揭示了 char 型別有號性、補碼表示與溢位行為的深層機制。

核心知識點拆解

1. char 的有號性實作定義

C 標準規定 char 是否為有號(signed char)或無號(unsigned char)屬於實作定義行為。主流 x86/x64 編譯器(GCC、MSVC、Clang)預設視為 signed char,範圍 [-128, 127]。可透過 -funsigned-char 或 /J 改變預設行為。

2. 補碼表示與溢位規則

有號整數採用二補碼表示:

  • -1 → 0xFF (1111 1111)
  • -128 → 0x80 (1000 0000) 最小值
  • 127 → 0x7F (0111 1111) 最大值

當計算結果超出範圍,發生有號整數溢位,屬於未定義行為。但主流編譯器採用「環繞」語意(截斷高位),即 -129 → 0x7F (127),-256 → 0x00 (0)。

3. 迴圈逐步演示

i計算值 -1-i補碼 (Hex)解釋為 signed char
0-10xFF-1 (非 '\0')
127-1280x80-128 (非 '\0')
128-1290x7F127 (非 '\0')
255-2560x000 ('\0')
256-2570xFF-1 (循環重複)

arr[255] 首次出現 '\0',strlen 以此為終止條件,故回傳 255。

關鍵教訓與防禦性程式設計

陷阱 1:隱含有號性假設

錯誤:假設 char 為無號,直接存儲 0~255 數值。
正確:明確使用 uint8_t (C99 <stdint.h>) 或 unsigned char 表達位元組資料。

陷阱 2:字串處理混用數值陣列

strlen、strcpy 等字串函式預期以 '\0' 結尾的字串。將數值陣列當字串處理,極易因資料中恰好出現 0 而提前截斷,或因無終止符導致緩衝區溢位。

陷阱 3:有號溢位是 UB

雖然主流編譯器環繞,但標準不保證。涉及邊界運算時,應轉換為較寬整型(int、long)計算後再截斷存回,或使用 unsigned 型別明確定義環繞語意。

修正版程式碼

#include <stdio.h>
#include <stdint.h>

int main() {
    uint8_t arr[1000];           // 明確無號 8-bit
    for (int i = 0; i < 1000; i++) {
        arr[i] = (uint8_t)(-1 - i);  // 定義明確的模 256 環繞
    }
    // 不再使用 strlen,改用已知長度或自訂結構
    printf("arr[255] = %u\n", arr[255]);  // 輸出 0
    return 0;
}

總結

這個經典面試題揭示了三大 C 語言核心概念:型別有號性實作定義、補碼溢位環繞、字串終止符約定。實務開發中:

  1. 處理位元組/二進制資料 → uint8_t / unsigned char
  2. 處理文字字串 → char + 確保 '\0' 終止
  3. 跨平台移植 → 永不假設 char 有號性,編譯選項固定或使用標準整型

理解底層位元模型,才能寫出可移植、無驚喜的系統級程式碼。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved