前言:
目前看官们对“c语言byte数组不能存0x00吗”大约比较注重,咱们都需要知道一些“c语言byte数组不能存0x00吗”的相关内容。那么小编也在网上汇集了一些对于“c语言byte数组不能存0x00吗””的相关内容,希望你们能喜欢,咱们快快来学习一下吧!现在的Windows操作系统有许多不同语言版本,可以支持所有国家现有的语言文字。这就涉及到不同字符集的编码规则。
本节必须掌握的知识点:
字符集
C语言款字符
宽字符和Windows
1.4.1 字符集
■ASCII码单字节字符集
现代计算机发源于美国,计算机最早支持的语言文字自然是美式英语。为了能够显示美式英语字符,美国人发明了一种ASCII码的编码规则。ASCII码采用7位二进制数定义一个符号,取值范围为0~127,对应128个字符。如图1-6所示。
ASCII码的编写具有一定规律,是一种非常可靠的编码,广泛应用于计算机的键盘、显示器、系统硬件、打印机、字体文件操作系统和互联网。
ASCII码只能满足美国的需要,但是对于世界其他国家的文字符号并不能满足。为了满足欧洲国家的需要,IBM公司使用8位二进制数表示字符,编写了扩展ASCII码表,最多支持256个字符。
为了统一标准,美国国家标准局(ANSI)制定了统一标准的ASCII码。我们称之为单字节字符集。
如果将计算机应用于东亚国家,如中文、韩文和日文,就需要编写多达几万个字符。因此即使8位二进制数也无法实现。为此,将二进制数扩展到16位,最多可以编写216个字符,基本就可以满足需要了。
■Unicode宽字节字符集
Unicode字符集为16位,最多可以支持65536个字符。 Unicode字符串中的所有字符都是16位的(两个字节)。这样,它就能够对世界各国的书面文字中的所有字符进行编码,远远超过了单字节字符集的256个字符的数目。但Unicode并非尽善尽美。Unicode字符的字符串占用的内存比ASCII字符串大两倍。
这65536个字符可以分成不同的区域。表1-1 显示了这样的区域的一部分以及分配给这些区域的字符。
16位代码
字符
16位代码
字符
0000-007F
ASCII
0300-036F
通用区分标志
0080-00FF
拉丁文字符
0400-04FF
西里尔字母
0100-017F
欧洲拉丁文
0530-058F
亚美尼亚文
0180-01FF
扩充拉丁文
0590-05FF
希伯莱文
0250-02AF
标准拼音
0600-06FF
阿拉伯文
02B0-02FF
修改型字母
0900-097F
梵文
2E80-2EFF
中日韩汉字部首补充
31C0-31EF
中日韩汉语笔画
表1-1 区域字符
目前尚未分配的代码点大约还有29 000个,不过它们是保留供将来使用的。另外,大约有 6 0 0 0个代码点是保留供个人使用的。
■字符编码UTF-8
Unicode 统一了所有字符的编码,是一个 Character Set,也就是字符集,字符集只是给所有的字符一个唯一编号,但是却没有规定如何存储,一个编号为 65 的字符,只需要一个字节就可以存下,但是编号40657的字符需要两个字节的空间才可以装下,而更靠后的字符可能会需要三个甚至四个字节的空间。
这时,用什么规则存储 Unicode 字符就成了关键,我们可以规定,一个字符使用四个字节存储,也就是 32 位,这样就能涵盖现有 Unicode 包含的所有字符,这种编码方式叫做 UTF-32(UTF 是 UCS Transformation Format 的缩写)。UTF-32 的规则虽然简单,但是缺陷也很明显,假设使用 UTF-32 和 ASCII 分别对一个只有西文字母的文档编码,前者需要花费的空间是后者的四倍(ASCII 每个字符只需要一个字节存储)。
在存储和网络传输中,通常使用更为节省空间的变长编码方式 UTF-8,UTF-8 代表 8 位一组表示 Unicode 字符的格式,使用 1 - 4 个字节来表示字符。
UTF-8 的编码规则如下(U+ 后面的数字代表 Unicode 字符代码):
U+ 0000 ~ U+ 007F: 0XXXXXXX
U+ 0080 ~ U+ 07FF: 110XXXXX 10XXXXXX
U+ 0800 ~ U+ FFFF: 1110XXXX 10XXXXXX 10XXXXXX
U+10000 ~ U+1FFFF: 11110XXX 10XXXXXX 10XXXXXX 10XXXXXX
可以看到,UTF-8 通过开头的标志位位数实现了变长。对于单字节字符,只占用一个字节,实现了向下兼容 ASCII,并且能和 UTF-32 一样,包含 Unicode 中的所有字符,又能有效减少存储传输过程中占用的空间。
当前Windows操作系统默认使用Unicode字符集。
1.4.2 C语言宽字符
由于本书采用C语言实现Windows程序,因此我们必须介绍一下C语言宽字符集函数。最早我们学习C语言时,使用的是ANSI C(美国国家标准程式设计语言——C),支持7位ASCII码字符。ANSI C 还支持多字节字符集,例如中文、日文和韩文版本 Windows 支持的字符集。然而,这些多字节字符集被当成单字节值的字符串。多字节字符集主要影响 C 语言运行库函数。相比之下,宽字符比正常字符宽,而且会引起一些编译问题。
宽字符不一定是 Unicode。Unicode 是一种可能的宽字符集。然而,因为本书的焦点是 Windows 而不是一个抽象的C语言实现,所以本书将把宽字符和 Unicode 作为同义词。
■ANSI字符集char数据类型
char c = 'A';
变量c需要一个字节来存储空间十六进制值0x41,表示ASCII码字母A。
再定义一个字符串指针:
char *p;
因为windows是32位系统,指针变量P需要4个字节的存储空间。
char *p = "Hello!";
字符串需要7个字节的空间存储,其中一个是字符串结尾的空字符。
还可以定义一个字符数组:
char a[10];
编译器保留10个字节的空间给这个数组。sizeof(a)表达式会返回10。
还可以写成:
char a[] = "Hello!";//初始化数组
■宽字符集char数据类型
使用Unicode宽字符并不会改变C语言中的字符数据类型。char类型继续代表1个字节的存储空间,而且sizeof(char)返回1。理论上来说,C语言中的一个字节可能长于8位,但是对于大多数人来说,一个字节就是8位宽。
C语言中的宽字符是基于wchar_t数据类型的。这个数据类型被定义在多个头文件中,包括WCHAR.H:
typedef usigned short wchar_t;
wchar_t数据类型和一个无符号短整型一样都是16位宽。
可以用下面的语句来定义一个包含单个宽字符的变量:
wchar_t c = 'A';
变量C是一个两个字节的值0X0041,这是Unicode字母A的代表。
由于Intel处理器存储多字节值时总是最低有效字节优先(小端存储模式),所以这些字节实际上在内存中式以0x41 ,0x00的顺序存储的。如果检查Unicode文本的内存存储,请务必记住这点。
wchar_t *p = L"Hello!";
大写字母L表示长整型,紧接左引号。这向编译器表明这个字符串将用宽字符存储,也就是说,每个字符占两个字节。指针变量p需要4个字节的存储空间,但这个字符串需要14个字节的存储空间。
wchar_t a[] = L"Hello!";
sizeof(a)同样返回14。
虽然看起来像是一个录入错误,但是引号之前的L非常重要,而且这两个字符之间绝对不能有空格。只有有了这个L,编译器才知道你想要字符串用用两个字节来存储一个字符。
wchar_t c = L'A';
单个字符而言,可以使用L前缀表示宽字符,也可以缺省,编译器会自动加0。
C语言标志库函数对应有ANSI字符集函数和宽字符集函数,详见下一小节中的表1-2。
1.4.3 Windows宽字符
■ANSI函数和UNICODE函数的兼容
在Windows中ANSI和UNICODE字符串操作分别提供了相应的函数,也提供了一套兼容两者的函数。比如:
举例
例1:
#ifdef UNICODE
#define _tcscpy wcscpy
#else
#define _tcscpy strcpy
#endif
例2:
#ifdef UNICODE
#define MessageBox MessageBoxW
#else
#define MessageBox MessageBoxA
#endif // !UNICODE
提示
1.Windows API函数定义的是Unicode函数,与此对应的ASCII函数是将ASCII字符转换为Unicode字符,然后再由Unicode函数实现。
2.Windows操作系统动态链接库中的函数名和动态链接库名为ASCII字符。
■Windows头文件的类型
WINDOWS.H头文件包含许多其他头文件,比如:
WINDEF.H头文件:基本数据类型的定义
WINNT.H头文件:负责处理基本的Unicode支持功能
WINNT.H头文件在开始位置包含了C的头文件CTYPE.H,CTYPE.H头文件中定义了wchar_t数据类型。而WINNT.H头文件中定义了两个新的数据类型CHAR和WCHAR:
typedef char CHAR;
typedef char WCHAR; //wc
CHAR和WCHAR是Windows推荐使用的数据类型,分别定义8位和16位的字符。WCHAR后面注释wc,是建议使用匈牙利标记法来说明这是一个基于WCHAR数据类型的变量,即这是一个宽字符。
接下来WINNT.H头文件定义了可用做8位字符串指针的6种数据类型和可用作const 8位字符串指针的4种数据类型。
typedef CHAR * PCHAR, * LPCH, * PCH, * NPSTR, * LPSTR, * PSTR ;
typedef CONST CHAR * LPCCH, * PCCH, * LPCSTR, * PCSTR ;
前缀N和L代表near和long,指16位windows系统中的两种大小不同的指针。但在win32中near和long指针则没有区别。
同样接下来WINNT.H头文件定义了可用做16位字符串指针的6种数据类型和可用作const 16位字符串指针的4种数据类型。
typedef WCHAR * PWCHAR, * LPWCH, * PWCH, * NWPSTR, * LPWSTR, * PWSTR ;
typedef CONST WCHAR * LPCWCH, * PCWCH, * LPCWSTR, * PCWSTR ;
这样我们就有了数据类型CHAR和WCHAR的各种指针。像TCHAR.H中一样,WINNT.H将TCHAR定义为一个通用的字符类型。如果标识符_UNICODE被定义了,则TCHAR和TCAHR的指针就被分别定义为WCHAR和指向WCHAR的指针。如果标识符UNICODE没有被定义,则TCHAR被定义为char类型或char类型的指针。
#ifdef UNICODE
typedef WCHAR TCHAR, * PTCHAR ;
typedef LPWSTR LPTCH, PTCH, PTSTR, LPTSTR ;
typedef LPCWSTR LPCTSTR ;
#else
typedef char TCHAR, * PTCHAR ;
typedef LPSTR LPTCH, PTCH, PTSTR, LPTSTR ;
typedef LPCSTR LPCTSTR ;
#endif
如果TCHAR已经在头文件中被定义了,WINNT.H和WCHAR.H头文件都能防止TCHAR数据类型被重复定义。WINNT.H头文件还定义了一个宏,它将L添加到一个字符串的第一个引号前。
#define __TEXT(quote) L##quote //UNICODE已定义
#define __TEXT(quote) quote //UNICODE未定义
而不管怎样,TEXT宏如下定义:
#define __TEXT(quote) __TEXT(quote)
这些定义可以让你在同一个程序中混合使用ASCII和Unicode字符串。
Windows NT从底层支持Unicode,意味着Windows NT内部使用16位字符的字符串。因为世界上还有许多地方不使用16位字符串,所以windows NT操作系统必须经常在内部转换字符串。在Windows NT上,既可以执行ASCII、UNICODE单写的程序,也可以执行为ASCII和Unicode混合编写的程序。其实是通过相关的API函数实现的。
■ Windows的字符串函数
Microsoft C包含了宽字符以及通用版的需要字符串参数的C语言运行库函数。不过,Windows复制了其中一部分C函数。
ILength = lstrlen (pString) ;
pString = lstrcpy (pString1, pString2) ;
pString = lstrcpyn (pString1, pString2, iCount) ;
pString = lstrcat (pString1, pString2) ;
iComp = lstrcmp (pString1, pString2) ;
iComp = lstrcmpi (pString1, pString2) ;
这些函数提供了与C运行库中对应的函数功能。当定义了UNICODE标识符时,这些函数就支持宽字符串,否则只接受常规字符串。
■在Windows中使用printf
遗憾的是在windows程序中不能使用printf函数,因为Windows不存在标准输入和标准输出的概念。但是在Windows程序中可以使用fprintf函数和sprintf函数。
函数原型:
int printf (const char * szFormat, ...) ;
int sprintf (char * szBuffer, const char * szFormat, ...) ;
sprintf函数并不是将格式化结果写到标准输出,而是将其存入szBuffer缓冲区。
printf ("The sum of %i and %i is %i", 5, 3, 5+3) ;
的功能等同于:
char szBuffer [100] ;
sprintf (szBuffer, "The sum of %i and %i is %i", 5, 3, 5+3) ;
puts (szBuffer) ;
【注意】缓冲区要足够的大。
sprintf函数还有一个变形函数vsprintf函数。
int sprintf (char * szBuffer, const char * szFormat, ...)
{
int iReturn ;
va_list pArgs ;
va_start (pArgs, szFormat) ;
iReturn = vsprintf (szBuffer, szFormat, pArgs) ;
va_end (pArgs) ;
return iReturn ;
}
参见C语言中可变参数的内容。
因为很多早期的windows程序使用了sprintf和vsprintf函数,所以windows API中增加了两个相似的函数wsprint和wvsprintf函数。
随着宽字符的引入,sprintf函数增加了许多,如下表:
ASCII
宽字符
通用
可变数目的参数
标准版
sprintf
swprintf
_stprintf
最大长度版
_sprintf
_snwprintf
_sntprintf
Windows版
wsprintfA
wsprintfW
wsprintf
参数数组的指针
标准版
vsprintf
vswprintf
_vstprintf
最大长度版
_vsnprintf
_vsnwprintf
_vsntprintf
Windows版
wvsprintfA
wvsprintfW
wvsprintf
表1-2 printf函数
在宽字符版的sprintf函数中,字符串缓冲区被定义为宽字符串。在所有的宽字符版的函数中,格式字符串必须是宽字符串。不过,确保传递给这些函数的其他字符串也是宽字符串则是你的责任。
■字符串处理函数常用函数对照表
ANSI
UNICODE
通用
说明
数据类型
(char.h)
(wchar.h)
(tchar.h)
char
wchar_t
TCHAR
char *
wchar_t *
TCHAR*
LPSTR
LPWSTR
LPTSTR
LPCSTR
LPCWSTR
LPCTSTR
字符串转换
atoi
_wtoi
_ttoi
把字符串转换成整数(int)
atol
_wtol
_ttol
把字符串转换成长整型数(long)
atof
_wtof
_tstof
把字符串转换成浮点数(double)
itoa
_itow
_itot
将任意类型的数字转换为字符串
字符串操作
strlen
wcslen
_tcslen
获得字符串的数目
strcpy
wcscpy
_tcscpy
拷贝字符串
strncpy
wcsncpy
_tcsncpy
类似于strcpy/wcscpy,同时指定拷贝的数目
strcmp
wcscmp
_tcscmp
比较两个字符串
strncmp
wcsncmp
_tcsncmp
类似于strcmp/wcscmp,同时指定比较字符字符串的数目
strcat
wcscat
_tcscat
把一个字符串接到另一个字符串的尾部
strncat
wcsncat
_tcsnccat
类似于strcat/wcscat,而且指定粘接字符串的粘接长度.
strchr
wcschr
_tcschr
查找子字符串的第一个位置
strrchr
wcsrchr
_tcsrchr
从尾部开始查找子字符串出现的第一个位置
strpbrk
wcspbrk
_tcspbrk
从一字符字符串中查找另一字符串中任何一个字符第一次出现的位置
strstr
wcsstr/wcswcs
_tcsstr
在一字符串中查找另一字符串第一次出现的位置
strcspn
wcscspn
_tcscspn
返回不包含第二个字符串的的初始数目
strspn
wcsspn
_tcsspn
返回包含第二个字符串的初始数目
strtok
wcstok
_tcstok
根据标示符把字符串分解成一系列字符串
wcswidth
获得宽字符串的宽度
wcwidth
获得宽字符的宽度
字符串测试
isascii
iswascii
_istascii
测试字符是否为ASCII 码字符, 也就是判断c 的范围是否在0 到127 之间
isalnum
iswalnum
_istalnum
测试字符是否为数字或字母
isalpha
iswalpha
_istalpha
测试字符是否是字母
iscntrl
iswcntrl
_istcntrl
测试字符是否是控制符
isdigit
iswdigit
_istdigit
测试字符是否为数字
isgraph
iswgraph
_istgraph
测试字符是否是可见字符
islower
iswlower
_istlower
测试字符是否是小写字符
isprint
iswprint
_istprint
测试字符是否是可打印字符
ispunct
iswpunct
_istpunct
测试字符是否是标点符号
isspace
iswspace
_istspace
测试字符是否是空白符号
isupper
iswupper
_istupper
测试字符是否是大写字符
isxdigit
iswxdigit
_istxdigit
测试字符是否是十六进制的数字
大小写转换
tolower
towlower
_totlower
把字符转换为小写
toupper
towupper
_totupper
把字符转换为大写
字符比较
strcoll
wcscoll
_tcscoll
比较字符串
日期和时间转换
strftime
wcsftime
_tcsftime
根据指定的字符串格式和locale设置格式化日期和时间
strptime
根据指定格式把字符串转换为时间值, 是strftime的反过程
打印和扫描字符串
printf
wprintf
_tprintf
使用vararg参量的格式化输出到标准输出
fprintf
fwprintf
_ftprintf
使用vararg参量的格式化输出
scanf
wscanf
_tscanf
从标准输入的格式化读入
fscanf
fwscanf
_ftscanf
格式化读入
sprintf
swprintf
_stprintf
根据vararg参量表格式化成字符串
sscanf
swscanf
_stscanf
以字符串作格式化读入
vfprintf
vfwprintf
_vftprintf
使用stdarg参量表格式化输出到文件
vprintf
使用stdarg参量表格式化输出到标准输出
vsprintf
vswprintf
_vstprintf
格式化stdarg参量表并写到字符串
sprintf_s
swprintf_s
_stprintf_s
格式化字符串
数字转换
strtod
wcstod
_tcstod
把字符串的初始部分转换为双精度浮点数
strtol
wcstol
_tcstol
把字符串的初始部分转换为长整数
strtoul
wcstoul
_tcstoul
把字符串的初始部分转换为无符号长整数
_strtoi64
_wcstoi64
_tcstoi64
输入和输出
fgetc
fgetwc
_fgettc
从流中读入一个字符并转换为宽字符
fgets
fgetws
_fgetts
从流中读入一个字符串并转换为宽字符串
fputc
fputwc
_fputtc
把宽字符转换为多字节字符并且输出到标准输出
fputs
fputws
_fputts
把宽字符串转换为多字节字符并且输出到标准输出串
getc
getwc
_gettc
从标准输入中读取字符, 并且转换为宽字符
getchar
getwchar
_gettchar
从标准输入中读取字符
putc
putwc
_puttc
标准输出
putchar
putwchar
_puttchar
标准输出
ungetc
ungetwc
_ungettc
把一个字符放回到输入流中
表1-3 字符串处理函数常用函数对照表
标签: #c语言byte数组不能存0x00吗 #未定义标识符_lpw #c语言6个字符宽度