C和C++安全编码(原书第2版)
上QQ阅读APP看书,第一时间看更新

2.1.6 字符类型

下面三种类型,char、signed char和unsigned char统称为字符类型。编译器可以自由地定义char,使它与signed char或unsigned char具有相同的范围、表示方式和行为。不管编译器作出的选择是什么,char都是独特的类型。

虽然没有在任何地方指出,但C标准选择字符类型遵从如下一致的理念。

signed char和unsigned char

·适用于小整数值

普通的char

·用于一个字符串字面值的每个元素的类型

·用于与整数的数据相对的字符数据(其中符号没有意义)

下面的程序片段显示了标准字符串处理函数strlen()分别被一个普通的字符串、一个有符号字符串和一个无符号字符串调用的情况。strlen()函数需要一个类型为const char*的参数。


1 size_t len;
2 char cstr[] = "char string";
3 signed char scstr[] = "signed char string";
4 unsigned char ucstr[] = "unsigned char string";
5
6 len = strlen(cstr);
7 len = strlen(scstr); /* warns when char is unsigned */
8 len = strlen(ucstr); /* warns when char is signed */

在符合“MSC00-C.在高警告级别没有警告地编译”的高警告级别进行编译时,在下列情况下会发生警告。

·当char有符号时,由unsigned char[]转换为const char *

·当char无符号时,由signed char[]转换为const char *

要消除这些警告,强制类型转换是必需的,但过多的强制类型转换会使代码很难阅读并隐藏正常的警告消息。

如果用一个C++编译器编译这段代码,由unsigned char[]转换为const char与*由signed char[]转换为const char *将被标记为需要强制类型转换的错误。为了与标准的窄字符串处理函数兼容,“STR04-C.使用普通char类型表示基本字符集中的字符”建议,使用普通的char。

int

int类型用于下面这种情况,数据可能是EOF(一个负值)或解释为unsigned char的字符,为防止出现符号扩展,于是把它转换为int。例如,在一个用32位值表示int类型的平台上,扩展ASCII码0xFF会当作00 00 00 FF传回。

·因此,fgetc()、getc()、getchar()、fgetwc()、getwc()和getwchar()都返回int。

·在中声明的字符分类函数,如isalpha(),接受int参数,因为可能会传给它们fgetc()或上述列表中其他函数的结果。

在C语言中,字符常量的类型为int。它的值是将普通的char转换为int的结果。这造成的也许令人惊讶的后果是,对于所有的字符常量c,sizeof c等于sizeof int。这也意味着,例如,当x是一个char类型的变量时,sizeof'a'不等于sizeof x。

在C++语言中,与在C中的情况不同,仅包含一个字符的一个字符字面值,其类型为char,因此,它的大小是1。在C和C++中,一个宽字符字面值都具有wchar_t类型,而一个多字符字面值的类型都为int。

unsigned char

当正在操作的对象可能是任何类型,而且有必要访问该对象的所有二进制位,比如用fwrite()时,unsigned char类型是有用的。不像其他的整数类型,unsigned char类型拥有独特的属性,即存储在unsigned char类型对象中的值,保证会当作一个纯粹的二进制表示法来表示属性值。C标准定义的纯粹二进制表示法为“一种使用二进制数字0和1的整数的位置表示法,其中,其值用连续二进制位乘以从1开始的2的连续整数次幂之和表示,除非此二进制位是最高位”。

unsigned char类型的对象都保证没有填充位并因此没有表示形式的陷阱。所以,任何类型的非二进制位域(non-bit-field)的对象都可以复制到一个unsigned char数组中(例如,通过memcpy()),并每次1个字节地检查它们的表示形式。

wchar_t

·宽字符用于自然语言的字符数据。

“STR00-C.使用一个适当的类型来表示字符”建议字符类型的使用遵循这个相同的理念。对于基本的字符集中的字符,它用哪个数据类型并没有关系,除非由于类型兼容的原因。