簡體   English   中英

為什么 C++ 中的 `const char *` 類型可以存儲 Unicode?

[英]Why `const char *` type in C++ can store Unicode?

我可以寫這樣的代碼:

const char * a = "你好";
cout<<a;

但是當這樣寫時:

char a[] = {'你','好'};
cout<<a;

它輸出這樣的亂碼:

在此處輸入圖片說明

我以為漢字存儲在wchar_t

那么const char *是如何包含漢字的呢?

當你寫char a[] = {'你','好'}; 它聲明了一個包含 2 個元素(即 2 個字符)的字符數組。 由於它不是以空值結尾的,因此它不是cout可以正確打印的字符串,並且嘗試打印它會調用未定義的行為。 但即使你添加一個空終止符{ '你', '好', '\\0' }; 它仍然無法工作,因為 1 字節char不能存儲漢字。 事實上,如果兩個單引號之間的內容超過 1 個字節(如本例中的'abcd''你' ),則行為是實現定義的 請參見C 和 C++ 中的多字符文字

但是,如果將字符括在雙引號"你好"那么它絕對不是3 字節以空字符結尾的字符串文字,而是某種編碼中的字節序列 C++ 標准沒有指定在字符串文字中使用哪種編碼,但通常是在其編碼中保存在源文件中的任何字節,這通常是 Windows 中的當前 ANSI 代碼頁和 Linux 中的 UTF-8。 std::string在里面包裝了一個const char*所以同樣的事情也適用於它

UTF-8 是一種變長編碼,與其他多字節編碼一樣,其單位是字節,所以它的底層表示可以是一個char[]數組,而"你好"將是一個由 6 個代碼單元組成的字符串。 您可以使用strlen()進行檢查。 OTOH cout對這些字符一無所知也不關心它是單字節字符還是更長的字符。 它只是將字節流傳遞給終端,終端的工作是將它們顯示在屏幕上。 但是如果它想要它可以很容易地確定字符的長度,就像終端或文本編輯器所做的那樣,因為它是在字符編碼中定義的


C++ 中還有許多其他字符類型: wchar_tchar8_tchar16_tchar32_t 它們對應的字符串類型是std::wstringstd::u8stringstd::u16stringstd::u32string

就像char*wchar_t*的編碼不是由標准定義的,但它在 Windows 中通常是UTF-16,在 Linux 中通常是UTF-32。 建議使用char8_tchar16_tchar32_t ,無論編譯器設置源文件編碼如何,它們都強制要求使用 UTF-8/16/32編碼

要在任何編碼之間進行轉換,您可以使用std::codecvt
在舊的 C++ 標准中還有不推薦使用的轉換器std::wstring_convert / std::codecvt_utf8 / std::codecvt_utf16 / std::codecvt_utf8_utf16和每個系統中的轉換例程: Unix 中的iconv和 Windows 中的WideCharToMultiByte / MultiByteToWideChar ,但它更好使用現代標准功能實現可移植性

你可能想閱讀這些

這里有幾個字符串編碼功能在起作用。 即:

1.字符編碼

有多種編碼字符串的方法。 char並不意味着 1 字節字符。 多字節字符集 (MBCS) 在 Unicode 出現之前已經存在了幾十年,這可能是您的編譯器解釋文字中文字符的方式。 如果您查看表示此字符串的內存,您幾乎肯定會看到每個字符由不止 1 個字節表示。

不過,這是一個常見的令人頭疼的問題,這也是 Unicode 被構思出來的原因。 為了正確的字符串表示,一切都需要使用相同的字符編碼。 保存在磁盤上的文本文件、編譯器、處理字符串的代碼(以及所有庫,如std:: )、寫入的流、字體……一切都需要就編碼達成一致。

在現代,我們通過使用某種形式的 Unicode 來避免這種頭痛。

最簡短的答案是,這取決於您的編譯器如何解釋您的源代碼。 它是實現定義的,通常有一種特定於編譯器的方式來指定此行為(對於 msvc: /utf-8 )。

這意味着您的第二個示例確實假設每個字符為 1 個字節,只有在您的編譯器使用這些字符適合單個字節的編碼進行操作時才能成功,我懷疑這是不可能的。 編譯器因此將截斷為 1 個字符,並且您基本上會得到垃圾。

2. 空終止

在 C 或 C++ 中,字符串通常以空字符結尾,這意味着在最后一個字符之后,值0表示字符串的結尾。 abc這樣的字符串在內存中表示為 4 個字節: 'a', 'b', 'c', 0

在您的第一個示例中,編譯器會自動為您添加空終止字符。

在您的第二個示例中,沒有空終止符。 因此,當您將字符串打印到控制台時,打印例程不知道您的字符串有多長,並且會一直打印,直到在垃圾內存中找到空值為止。

當您在代碼中編寫字符串文字時,使用長度超過 1 個字節的字符,編譯器會為您進行轉換。 考慮一下:

const char * a = "你好";
cout << strlen(a); // Prints 6

std::cout按原樣打印字節,Windows 終端識別字符。

對於字符數組,即使添加缺失的零,也可能無法完成類似的轉換。 這是實現定義的行為。 例如,在我使用的編譯器中,每個字符都被解釋為int類型的多字符文字,然后被截斷為 1 字節的char類型。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM