核心差異一覽
| 特性 | C# char | C/C++ char | C/C++ wchar_t |
| 大小 | 2 位元組 (16 bits) | 1 位元組 (8 bits) | 2 位元組 (Windows) / 4 位元組 (Linux) |
| 編碼 | UTF-16 代碼單元 | ANSI/ASCII (視編譯器/地區設定) | UTF-16 (Windows) / UTF-32 (Linux) |
| 字元範圍 | U+0000 ~ U+FFFF (基本多文種平面) | 0 ~ 255 (擴展 ASCII) | 完整 Unicode 碼位 |
| 字串型別 | string (不可變 UTF-16 序列) | char* / std::string (位元組序列) | wchar_t* / std::wstring |
| 字面值語法 | 'A', '中', '\u4E2D' | 'A', '中' (需原始碼編碼一致) | L'A', L'中', u'A' (C++11 char16_t) |
C# char 為何是 2 位元組?
Console.WriteLine(sizeof(char)); // 輸出 2
Console.WriteLine("A".Length); // 輸出 1
Console.WriteLine("中".Length); // 輸出 1 (單一 UTF-16 代碼單元)
Console.WriteLine("😀".Length); // 輸出 2 (代碼對 surrogate pair)
char 代表單一 UTF-16 代碼單元,非完整 Unicode 字元 (Grapheme Cluster)。
- 補充平面字元 (Emoji、罕用漢字) 需 2 個
char (Surrogate Pair) 表示。
- .NET
string 內部為 UTF-16 編碼,Length 回傳代碼單元數,非字元數。
C/C++ char 陷阱
// C++
#include
int main() {
std::cout << sizeof(char) << std::endl; // 1
std::cout << sizeof(wchar_t) << std::endl; // 2 (Windows/MSVC) 或 4 (Linux/GCC)
char c = '中'; // 警告:截斷!僅保留低位元組
wchar_t wc = L'中'; // 正確
std::cout << c << " " << wc << std::endl;
}
char 歷史包袱:早期為 ASCII,後擴展為各地區代碼頁 (CP936/CP950/CP1252 等),非可攜式。
- 現代 C++ (C++11+) 建議使用
char16_t (UTF-16)、char32_t (UTF-32)、std::u16string/std::u32string。
- Windows API 使用
WCHAR (即 wchar_t) 系列函數 (MessageBoxW 等) 支援 Unicode。
跨語言互操作 (P/Invoke) 關鍵對應
| C# 型別 | C/C++ 型別 | MarshalAs | 適用場景 |
string (In) | const wchar_t* (LPCWSTR) | [MarshalAs(UnmanagedType.LPWStr)] | 預設字串封送 (Unicode) |
string (In) | const char* (LPCSTR) | [MarshalAs(UnmanagedType.LPStr)] | ANSI 介面 (舊版 API) |
StringBuilder (Out) | wchar_t* (LPWSTR) | [MarshalAs(UnmanagedType.LPWStr)] | 接收 Unicode 字串 |
char | wchar_t / WCHAR | 自動 | 單字元傳遞 |
byte / sbyte | char / signed char | 自動 | ANSI 字元/位元組 |
SQL Server 對應關係
| SQL Server 型別 | 儲存內容 | .NET 讀取型別 |
char(n) | 定長非 Unicode (代碼頁) | string (自動轉 UTF-16) |
nchar(n) | 定長 Unicode (UCS-2/UTF-16) | string |
varchar(n) | 變長非 Unicode | string |
nvarchar(n) | 變長 Unicode | string (推薦預設) |
現代應用一律使用 nvarchar,避免代碼頁轉換錯誤與資料遺失。
常見錯誤與避坑
- 假設 char = 1 byte:C# 中
sizeof(char)==2,寫入二進位檔案時用 BinaryWriter.Write(char) 寫 2 位元組。
- 字串長度 ≠ 字元數:
"😀".Length == 2,計算顯示寬度請用 StringInfo 或 System.Globalization。
- P/Invoke 忘記 MarshalAs:預設封送字串為
LPWStr (Unicode),呼叫 ANSI API 必須加 [MarshalAs(UnmanagedType.LPStr)]。
- C++ 字串字面值編碼:原始碼儲存編碼 (UTF-8/GB2312) 決定
"中" 在記憶體位元組,建議原始碼統一 UTF-8 並加 u8 前綴 (C++20)。