// 代碼 1: 使用 \t
printf("Name\tAge\n");
printf("Alice\t18\n");
printf("Bob\t20\n");
// 輸出:
// Name Age
// Alice 18
// Bob 20 <-- 對齊!
// 代碼 2: 使用空格
printf("Name Age\n");
printf("Alice 18\n");
printf("Bob 20\n");
// 輸出:
// Name Age
// Alice 18
// Bob 20 <-- 錯位!
核心差異:**空格是固定寬度字符,\t 是「移動到下一個製表位」的控制字符。**
C 標準僅規定 \t 為「水平製表符」,**未規定其具體寬度**。實際表現完全取決於**終端模擬器 / 控制台 / 文本編輯器**的實現。
絕大多數終端 (Linux Terminal, Windows CMD/PowerShell, VS Code 終端) 默認設置**每 8 列為一個製表位** (第 1, 9, 17, 25... 列,下標從 0 或 1 起算)。
算法:當遇到 \t 時,光標向右移動,**直到到達下一個製表位列號**。
公式:若當前光標列為 col (0-based),製表位寬度 TS=8,則移動 (TS - (col % TS)) 格。若 col % TS == 0,移動 TS 格。
| 行 | 輸出內容 | 字符寬度 | \t 前列號 | 移動格數 | Age 起始列 |
|---|---|---|---|---|---|
| 1 | "Name\t" | 4 | 4 | 8-4=4 | 8 |
| 2 | "Alice\t" | 5 | 5 | 8-5=3 | 8 |
| 3 | "Bob\t" | 3 | 3 | 8-3=5 | 8 |
**結果**:三行 "Age" 均從第 8 列開始,**完美對齊**。
空格只是佔據一列。"Name" 4字符 + 2空格 = 第6列;"Alice" 5字符 + 1空格 = 第6列;"Bob" 3字符 + 3空格 = 第6列。**只有當所有字段寬度固定且小於製表位間距時,手動空格才能對齊**。一旦數據長度不固定,空格方案徹底崩潰。
sys.displayhook 影響。tabstop=4,導致同樣代碼在終端對齊、編輯器錯位。printf("中文\tEnglish\n"); // "中文" 佔 2 列 (終端通常雙寬)
printf("English\t中文\n");
若終端正確支持東亞寬度,"中文" 佔 2 列,\t 計算基於**列號而非字節數**,仍能對齊。但部分簡陋終端按字節計算,導致中文對齊失效。
唯一可靠、跨平台、可控的對齊方案:
// %-10s: 左對齊,佔 10 列,不足補空格
// %10s: 右對齊
printf("%-10s %5d\n", "Alice", 18);
printf("%-10s %5d\n", "Bob", 20);
printf("%-10s %5d\n", "Charlie", 25);
// 輸出:
// Alice 18
// Bob 20
// Charlie 25
配合 *.*s 限制最大寬度防溢出:printf("%-10.10s", long_str);
libtabulate、ctable 或自寫簡易表格渲染器。\t 通過**終端製表位機制**實現動態對齊,本質是「跳到下一個 8 的倍數列」。printf("%-Ns", ...) 寬度修飾符或結構化序列化格式。