現象重現
[c]
#include
#include
int main() {
char arr[1000];
int i;
for (i = 0; i < 1000; i++) {
arr[i] = -1 - i; // 關鍵:賦負值給 char
}
printf("%d\n", strlen(arr)); // 輸出 255 (而非 999 或 1000)
return 0;
}
為什麼是 255?
char 預設有號性由實現定義:x86 GCC/MSVC 預設 signed char (範圍 -128~127)。
- 補碼表示:-1 = 0xFF, -2 = 0xFE, ..., -128 = 0x80。
- 溢位環繞:
i=127 → arr[127]=-128 (0x80);i=128 → -129 需 9 位,截斷為 0x7F (+127);i=255 → -256 截斷為 0x00 (\0)。
strlen 遇 \0 即停止:arr[255] 首個為 0,故長度 255。
記憶體內容演變表
| i | 計算值 | 補碼 (hex) | 有號解讀 | 備註 |
| 0 | -1 | 0xFF | -1 | |
| 127 | -128 | 0x80 | -128 | 最小負數 |
| 128 | -129 | 0x7F | +127 | 溢位,符號翻轉 |
| 255 | -256 | 0x00 | 0 | 首個 \0,strlen 停這裡 |
| 256 | -257 | 0xFF | -1 | 循環重複 |
避坑指南
- 明確指定有號性:用
signed char / unsigned char / uint8_t (需 <stdint.h>) 替代裸 char 做數值運算。
- 字串緩衝區用
unsigned char 或 uint8_t:避免負值被誤判為結束符。
- 位運算、網路協議、二進制解析:一律用無號型別。
- 編譯器選項:GCC
-funsigned-char / MSVC /J 可強制 char 為無號,但**不建議依賴此選項**,源碼應自說明。
延伸:strlen vs sizeof
[c]
char buf[1000] = "hello";
printf("strlen=%zu, sizeof=%zu\n", strlen(buf), sizeof(buf)); // 5 vs 1000
sizeof:編譯期常數,陣列總位元組數。
strlen:執行期掃描首個 \0,不含結束符。