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

C語言 printf 格式符陷阱:%u 輸出 -16 的驚人結果

     編輯:C 語言問答

經典面試題深度解析:無符號格式符輸出有符號負數

題目回顧

printf("%d,%d,%u\n", 016, 16, -16);
// 前兩個輸出:14, 16
// 第三個 %u 輸出 -16 會是什麼?

核心知識點

常量/表達式類型/值說明
016八進制 int = 14前導 0 表示八進制:1×8 + 6 = 14
16十進制 int = 16默認十進制
-16有符號 int = -16二進制補碼表示
%u無符號十進制格式將對應內存位模式按無符號數解讀

內存位模式分析(32 位系統)

-16 的二進制補碼表示:
原碼: 00000000 00000000 00000000 00010000 (16)
反碼: 11111111 11111111 11111111 11101111
補碼: 11111111 11111111 11111111 11110000  ← 內存中實際存儲的位模式

%u 將這 32 位按無符號數解讀:
2³² - 16 = 4,294,967,296 - 16 = 4,294,967,280
十六進制: 0xFFFFFFF0

完整驗證程序

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

int main() {
    int a = 016;      // 八進制 14
    int b = 16;       // 十進制 16
    int c = -16;      // 有符號 -16
    unsigned int u = -16;  // 隱式轉換
    
    printf("原始題目輸出:\n");
    printf("%%d,%%d,%%u: %d,%d,%u\n\n", a, b, c);
    
    printf("詳細分析:\n");
    printf("a = 016 (八進制) = %d (十進制)\n", a);
    printf("b = 16 (十進制) = %d\n", b);
    printf("c = -16 (有符號) = %d\n", c);
    printf("c 的十六進制: 0x%X\n", c);
    printf("c 作為無符號: %u\n", (unsigned int)c);
    printf("UINT_MAX = %u\n", UINT_MAX);
    printf("UINT_MAX + 1 - 16 = %u\n\n", (UINT_MAX + 1u) - 16);
    
    printf("相關現象:\n");
    printf("-1 作為 %%u: %u (即 UINT_MAX)\n", -1);
    printf("-1 作為 %%x: 0x%X\n", -1);
    printf("關係: %u = %u + 1 + (-16) = %u\n", (unsigned int)(-16), UINT_MAX, (unsigned int)(-16));
    
    return 0;
}

執行結果

原始題目輸出:
%d,%d,%u: 14,16,4294967280

詳細分析:
a = 016 (八進制) = 14 (十進制)
b = 16 (十進制) = 16
c = -16 (有符號) = -16
c 的十六進制: 0xFFFFFFF0
c 作為無符號: 4294967280
UINT_MAX = 4294967295
UINT_MAX + 1 - 16 = 4294967280

相關現象:
-1 作為 %u: 4294967295 (即 UINT_MAX)
-1 作為 %x: 0xFFFFFFFF
關係: 4294967280 = 4294967295 + 1 + (-16) = 4294967280

核心結論

  1. %u 不做數值轉換:只是將內存中的位模式按無符號整數規則解讀並輸出
  2. 有符號轉無符號規則:C 標準規定,負數轉無符號時加 2ⁿ(n 為位寬),即模 2ⁿ 運算
  3. 實際輸出:4294967280(32 位系統),而非 16

工程避坑指南

危險場景後果修正方案
printf("%u", -1)輸出巨大正數,邏輯判斷錯誤用 %d 輸出有符號數,或顯式轉換 (unsigned)
比較 signed/unsigned-1 > 0U 為真!隱式轉換陷阱編譯器開啟 -Wsign-compare,統一類型
scanf("%u", &signed_var)輸入大數導致溢出/負數匹配格式符與變量類型,用 strtoul 解析

記憶口訣

「格式符只管解讀位模式,不做數值轉換;有符號負數遇 %u,加 2 的 N 次方變正數」

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