在C語言中,我們使用char來定義字符,占用一個字節,最多只能表示128個字符,也就是ASCII碼中的字符。計算機起源於美國,char 可以表示所有的英文字符,在以英語為母語的國家完全沒有問題。
但是世界上存在很多不同的語言,例如漢語、漢語、日語等有成千上萬個字符,需要用多個字節來表示,稱之為寬字符(Wide Character)。Unicode 是寬字符編碼的一種,已經被現代計算機指定為默認的編碼方式,Windows 2000以后的操作系統,包括Windows 2000、XP、Vista、Win7、Win8、Win10、Windows Phone、Windows Server 等(它們統稱為 Windows NT)都從底層支持Unicode,存取效率比 char 要高。
更多內容請查看:ASCII編碼與Unicode編碼
C語言中的寬字符
在C語言中,使用wchar.h
頭文件中的wchar_t
來定義寬字符,例如:
wchar_t ch = 'A';
wchar_t 被定義為typedef unsigned short wchar_t
,和一個無符號整型一樣,占用兩個字節。
如果定義寬字符串,需要加前綴L
,例如:
wchar_t *str = L"C語言中文網";
L
是必須要加的,並且與字符串之間不能有空格,只有這樣編譯器才知道每個字符占用兩個字節。
寬字符示例:
- #include <stdio.h>
- #include <wchar.h>
- int main(){
- char ch = 'A';
- wchar_t wch = 'A';
- char str[] = "C語言中文網";
- wchar_t wstr[] = L"C語言中文網";
- printf("ch=%d, wch=%d, str=%d, wstr=%d\n", sizeof(ch), sizeof(wch), sizeof(str), sizeof(wstr));
- return 0;
- }
運行結果:
ch=1, wch=2, str=12, wstr=14
wstr 之所以比 str 多兩個字節是因為:字符 'C' 占用兩個字節,字符串結束標志 '\0' 也占用兩個字節。
寬字符串的長度
計算ASCII字符串長度使用 strlen 函數,計算寬字符串長度使用 wcslen 函數:
- #include <stdio.h>
- #include <wchar.h>
- #include <string.h>
- int main(){
- char str[] = "C語言中文網";
- wchar_t wstr[] = L"C語言中文網";
- printf("strlen(str)=%d, wcslen(wstr)=%d\n", strlen(str), wcslen(wstr));
- return 0;
- }
運行結果:
strlen(str)=11, wcslen(wstr)=6
strlen 的運行結果顯然不正確,因為它把一個字節作為一個字符計算,而 wcslen 把兩個字節作為一個字符計算。
注意:wcslen 在 string.h 和 wchar.h 頭文件中均有說明。
維護一個版本的源代碼
在 Windows NT 以前的操作系統中,甚至包括 Windows 98,對寬字符的支持都不是很好,所以大多情況下使用ASCII編碼。Windows NT 推出以后,已經從底層支持了Unicode,所以在 Windows NT 上的程序大多使用Unicode。
如果你希望程序能夠在各種版本的Windows操作系統中運行,那么就需要維護兩個版本的源代碼,ASCII 版和 Unicode 版。ASCII 字符和 Unicode 字符的定義、使用都不一樣,要想在一個版本的源代碼中做兼容處理會非常困難,要做大量的工作,對程序員來說簡直是噩夢。
不過,Windows 又為我們做了一件好事,已經處理了兼容性問題。它是怎么做到的呢?
例如對於字符串,ASCII 中使用 char 來定義,而 Unicode 中使用 wchar_t 來定義,並且需要添加前綴L
。那么在 windows.h 頭文件中(或者是它包含的其他頭文件)就這樣來處理:
- #ifdef UNICODE
- typedef wchar_t TCHAR;
- #define TEXT(quote) L##quote
- #else
- typedef char TCHAR
- #define TEXT(quote) quote
- #endif
我們在源碼中可以這樣來使用:
TCHAR str[] = TEXT("C語言中文網");
如果是Unicode版,也就是定義了UNICODE宏,那么上面的語句等價於:
wchar_t str[] = L"C語言中文網";
如果是ASCII,也就是沒有定義UNICODE宏,那么等價於:
char str[] = "C語言中文網";
在Windows中,隨處可見這樣的處理。雖然現代操作系統都已經支持Unicode,無需再考慮與ASCII的兼容性問題,但是依然要為這些歷史問題付出代價。
總結:由於各種各樣的原因,我們優先使用Windows定義的數據類型、宏、結構體等,這樣編寫的程序兼容性較好,不用考慮ASCII和Unicode的問題。但這也帶來了一個挑戰,就是要熟悉Window定義的數據類型、宏、結構體等。