我正在阅读 Stephen G. Kochan 撰写的关于 C 编程的书 Programming in C。它指出:
“如果使用的字符值不是标准字符的一部分,则在转换为整数时可能会扩展其符号”
然后它说
“C 语言允许将字符变量声明为无符号,这避免了这个潜在的问题”
有人可以解释在从 char 转换为 int 期间扩展符号时可能会出现什么问题吗?为什么这很重要?从字符转换为负整数有什么问题?
谢谢你
我正在阅读 Stephen G. Kochan 撰写的关于 C 编程的书 Programming in C。它指出:
“如果使用的字符值不是标准字符的一部分,则在转换为整数时可能会扩展其符号”
然后它说
“C 语言允许将字符变量声明为无符号,这避免了这个潜在的问题”
有人可以解释在从 char 转换为 int 期间扩展符号时可能会出现什么问题吗?为什么这很重要?从字符转换为负整数有什么问题?
谢谢你
假设您从 <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。这很简单,我不明白大惊小怪。为什么我还要警告程序员? !”
现在isupper()
用 -124 而不是 132 调用。
但这有什么问题呢?什么都没有,除了编译器附带的 C 库isupper()
使用一个简单的 128 字节数组实现:它只是返回给定索引处的值。除了大写的 ASCII 码为 1 外,该数组在任何地方都初始化为 0。这样一个简单而优雅的实现......
但是等等,如果你将一个负值传递给这个函数会发生什么?好吧,这是不允许的:
c 参数是一个 int,应用程序应确保其值是可表示为无符号字符的字符或等于宏 EOF 的值。如果参数有任何其他值,则行为未定义。
所以,未定义的行为。在这种情况下,它会尝试访问不属于该进程的内存,然后 BAM!程序崩溃。
所以你看,char 是邪恶的,你不应该使用它,除非你真的了解如何正确使用它。
(*) 正如 Keith Thompson 在评论中所说,当然不可能避免使用char
. 从strlen()
到curl_easy_escape()
,大家用char
。但是您应该注意转换为int
,尤其是当char
可能包含负数时。<ctype.h> 函数和数组索引是容易犯代价高昂错误的两个地方。
在 C 中,plain char
可以是有符号或无符号的,选择权留给实现。
从C99、6.2.5、7开始:
char、signed char 和 unsigned char 这三种类型统称为字符类型。实现应将 char 定义为与有符号字符或无符号字符具有相同的范围、表示和行为。
因此,当将字符分配给整数时,是否设置了 char 的符号位会产生歧义,因为它会影响分配普通char 的整数的结果值。
我相信,书中引用的文字是指这一点,并且unsigned char
明确地使用可以避免这个问题。