本声明:
if('z' - 'a' == 25)
不保证以相同的方式评估。它取决于编译器。此外,不保证以与此相同的方式进行评估:
#if 'z' - 'a' == 25
即使预处理器和编译器都在同一台机器上运行。那是为什么?
答案 0 :(得分:69)
OP正在询问标准的直接引用 - N1570 §6.10.1p3,4 + footnote 168:
...根据6.6的规则评估控制常数表达式。 ...这包括解释字符常量,这可能涉及将转义序列转换为执行字符集成员。这些字符常量的数值是否与表达式中出现相同字符常量时获得的值匹配(#if或#elif指令除外)是实现定义的。 168
[脚注168]因此,以下#if指令和if语句中的常量表达式无法保证在这两个上下文中计算为相同的值。
#if 'z' - 'a' == 25 if ('z' - 'a' == 25)
所以,是的,它确实无法保证。
要理解为什么无法保证,首先您需要知道C标准不要求字符常量'a'
和'z'
具有数字通过ASCII分配给这些字符的值。 现在大多数 C实现使用ASCII或超集,但还有另一种名为EBCDIC的编码仍然被广泛使用(仅在IBM大型机上,但仍有很多那些) 。在EBCDIC中,'a'
和'z'
不仅具有与ASCII不同的值,字母表不是连续的序列!这就是为什么表达式'z' - 'a' == 25
可能不会首先评估为真。
您还需要知道C标准试图区分用于源代码的文本编码(“源字符集”)和程序将在运行时使用的文本编码(“执行字符集” “)。这是至少原则上你可以采用一个程序,其源代码以ASCII文本编码,并在使用EBCDIC的计算机上不加修改地运行,只需通过适当的交叉编译;您不必先将源文本转换为EBCDIC。
现在,编译器必须理解这两个字符集是否不同,但历史上,C预处理器(translation phases 1到4)和“编译器正确”(阶段5到7)是两个独立的程序和#if
表达式是预处理器必须知道执行字符集的唯一地方。因此,通过使实现定义预处理器使用的“执行字符集”是否与编译器正确使用的相匹配,标准许可预处理器在源字符集中完成所有工作,在1989年让生活变得更加轻松。
说了这么多,我会非常惊讶地发现一个现代编译器没有使两个表达式都被评估为相同的值,即使执行和源字符集非常不兼容。现代编译器往往具有集成的预处理器 - 阶段1到7都由同一个程序执行 - 即使它们不执行,也需要专门处理预处理器以匹配其执行字符的工程负担现在设置为编译器是微不足道的。
答案 1 :(得分:16)
因为并非所有计算机都使用ascii或unicode。
过去,一种名为ebcdic的标准很常见。在ebcdic 500中,'z'
的值为169,'a'
的值为130.表达式'z'-'a'
将评估为39.
这解释了为什么您不能为'a'
或甚至'z'-'a'
类型的表达式假设某个值。但是,它没有解释为什么Q中的两个表达式不能保证相等。
预处理器和编译器是两回事。预处理器处理源代码中使用的编码,而编译器则针对您正在编译的机器。有关更详细的解释,请参阅zwol的答案。
答案 2 :(得分:7)
为了扩展其他正确的答案,仍在使用的非ASCII C编译器的真实示例是IBM’s z/OS XL C/C++。默认情况下,它假定源文件位于IBM代码页1047(EBCDIC的版本与Latin-1具有相同的库)中。但是,它有几种不同的编译器选项,不仅支持ASCII,还支持“混合代码”,或包含多个编码数据的源文件。 (这些程序的存在是因为MVS编译器要求语法语句仅采用IBM-1047编码。)
从文档中可以看出,像#pragma CONVLIT(suspend)
这样的命令可能会使这两个语句在该编译器上的评估方式不同。我没有副本来测试MCVE。