为什么减法字符具体执行的行为?

这个说法:

if('z' - 'a' == 25) 

不保证以相同的方式进行评估。 它依赖于编译器。 另外,不保证以与此相同的方式进行评估:

 #if 'z' - 'a' == 25 

即使预处理器和编译器都在同一台机器上运行。 这是为什么?

OP在询问标准的直接引用 – N1570§6.10.1p3,4+脚注168 :

…控制常量expression式按照6.6的规则进行评估。 …这包括解释字符常量,这可能涉及将转义序列转换为执行字符集成员。 这些字符常量的数值是否匹配在expression式中出现相同的字符常量(除#if或#elif指令之外)时获得的值是否是实现定义的。 168

因此,以下#if指令和if语句中的常量expression式不能保证在这两个上下文中求值为相同的值。

 #if 'z' - 'a' == 25 if ('z' - 'a' == 25) 

所以,是的,它并不能保证。

为了理解为什么不能保证,首先你需要知道C标准不要求字符常量'a''z'具有通过ASCII分配给这些字符的数字值。 现在大多数 C实现使用ASCII或超集,但是还有另一种编码称为EBCDIC ,它仍然被广泛使用(只在IBM大型机上,但仍然有很多)。 在EBCDIC中, 'a''z'不同于ASCII的值不同,字母不是连续的序列! 这就是为什么expression式'z' - 'a' == 25可能不会首先评估真实性的原因。

您还需要知道C标准试图保持用于源代码的文本编码(“源字符集”)和程序在运行时使用的文本编码(“执行字符集”)之间的区别。 这样,至less在原则上,您可以采用一个程序,其源代码以ASCII文本编码,并在使用EBCDIC的计算机上不加修改地运行,只需交叉编译; 您不必先将源文本转换为EBCDIC。

现在,如果编译器不同,编译器必须理解这两个字符集,但从历史上看,C预处理器( 翻译阶段 1到4)和“编译器本身”(阶段5到7)是两个独立的程序,而#ifexpression式是预处理器必须知道执行字符集的唯一地方。 因此,通过使预处理器使用的“执行字符集”与编译器使用的“执行字符集”匹配,标准使预处理器在字符集中完成所有工作,使生活变得容易一些1989年。

说了这么多,即使执行和源字符集非常不兼容,我也会惊奇地发现一个现代编译器不能使两个expression式的值相同。 现代编译器倾向于集成预处理器 – 阶段1到阶段7都是由相同的程序执行 – 即使不是这样,专业化预处理器将其执行字符集与编译器相匹配的工程负担也是微不足道的如今。

因为不是所有的电脑都使用ascii或unicode。

过去,ebcdic的标准很常见。 在ebcdic 500中, 'z'的值是169, 'a'的值是130.然后expression式'z'-'a'将被评估为39。

这就解释了为什么你不能为'a'或者'z'-'a'这个types的expression假定某个值。 但是,这并不能解释为什么Q中的两个expression式不能保证相等。

预处理器和编译器是两个不同的东西。 预处理器处理源代码中使用的编码,而编译器则针对正在编译的机器。 请参阅zwol的答案以获得更详细的解释。

为了扩展其他正确的答案,仍然使用的非ASCII C编译器的一个真实例子是IBM的z / OS XL C / C ++ 。 默认情况下,它假定源文件位于IBM代码页1047(具有与Latin-1相同的库的EBCDIC版本)。 但是,它有几种不同的编译器选项,不仅支持ASCII,而且还支持“混合代码”或包含多种编码数据的源文件。 (这些程序的存在是因为MVS编译器仅需要使用IBM-1047编码的语法语句。)

从文档看,似乎可能会用像#pragma CONVLIT(suspend)这样的命令来#pragma CONVLIT(suspend) ,这种方式确实会使这两个语句在该编译器上的评估方式不同。 我没有一个副本来testing一个MCVE。