我正在阅读Stephen G. Kochan关于C编程的C编程。它声明:
“如果使用的字符值不是标准字符的一部分,则在转换为整数时可能会扩展其符号”
然后说明
“C语言允许声明字符变量无符号,这可以避免这个潜在的问题”
有人可以解释在从char转换为int期间扩展符号时可能出现的问题吗? 为什么这很重要? 一个负整数是什么问题,它被转换为char?
谢谢
答案 0 :(得分:4)
假设您从< ctype.h>,isupper()
中获取无辜的功能。
它定义为int isupper(int c);
。所以它需要一个int并返回一个int。
现在,让我们说你不是一个非常谨慎的程序员,你只需要将char传递给这个函数。你自己想:“会出现什么问题?这是我所知道的最简单的功能!”。
但你错了。在某个地方,由于这个可怕的错误,有人会让她的MP3播放器进入无休止的崩溃循环。
这就是原因。 C中最讨厌的类型是char。它可以是签名的,它可以是无符号的,你可以用这种或那种方式强制编译器(但是你打开另一种蠕虫),最糟糕的是,标准的C库在任何地方都使用这种类型!
所以,你使用char,但是你不知道它实际上是在你的环境中签名的。您可以使用它,好像世界是一个ASCII世界。
但世界不是。 MP3快乐的老板现在正在收听一首着名的德国歌曲,其名字中包含字母ä(“扩展的ASCII码132”)。
您将此字符传递给isupper()
,编译器会执行以下操作:
“啊,这是一个字符,但函数需要一个整数。我知道!我不会警告程序员,因为这太简单了。我只是将字符转换为整数并传递它。我该怎么做?让我们检查一下C标准...嗯...简单,只需取值并对其进行符号扩展(因为char已签名,你不知道吗?)。现在,这个字符的值为-124,所以我'我只是将它转换为一个值为-124的int。这很简单,我看不出大惊小怪。我为什么要警告程序员?!“
现在使用-124而不是132来调用isupper()
。
但那有什么问题?没有什么,除了编译器附带的C库使用一个简单的128字节数组实现isupper()
:它只返回给定索引处的值。除了大写的ASCII代码之外,数组初始化为0,其中它是1.这样一个简单而优雅的实现......
但是等一下,如果你把负值传递给这个函数怎么办?嗯,这是不允许的:
c参数是一个int,应用程序的值 ensure是一个可表示为unsigned char或等于的字符 宏EOF的值。如果参数有任何其他值,则 行为未定义。
所以,未定义的行为。在这种情况下,它尝试访问不属于该进程的内存,以及BAM!程序崩溃。
所以你看,char是邪恶的,你永远不应该使用它,除非你真的明白如何正确使用它。
(*)正如Keith Thompson在评论中所说,当然不可能避免使用char
。从strlen()
到curl_easy_escape()
,每个人都使用char
。但是您应该知道转化为int
,尤其是当char
可能包含负数时。 <文件ctype.h>函数和数组索引是两个容易出错的地方。
答案 1 :(得分:1)
在C中, plain char
可以是有符号或无符号的,可以选择实现。
来自 C99,6.2.5,7 :
共有三种类型char,signed char和unsigned char 称为字符类型。实现应定义char to 具有与签名相同的范围,表示和行为 char或unsigned char。
因此,当一个字符被赋值为整数时,是否设置了char的符号位是否存在歧义,因为它会影响分配了 plain char的整数的结果值。
我相信,本书中引用的文字引用了这一点,使用unsigned char
明确地避免了这个问题。