wil*_*rxx 41 c character-encoding
这个说法:
if('z' - 'a' == 25)
Run Code Online (Sandbox Code Playgroud)
不保证以同样的方式评估.它取决于编译器.此外,不保证以与此相同的方式进行评估:
#if 'z' - 'a' == 25
Run Code Online (Sandbox Code Playgroud)
即使预处理器和编译器都在同一台机器上运行.这是为什么?
zwo*_*wol 69
OP正在询问标准的直接引用 - N1570§6.10.1p3,4+脚注168:
...控制常数表达式根据6.6的规则进行评估....这包括解释字符常量,这可能涉及将转义序列转换为执行字符集成员.这些字符常量的数值是否与表达式中出现相同字符常量时获得的值匹配(#if或#elif指令除外)是实现定义的.168
[脚注168]因此,以下#if指令和if语句中的常量表达式无法保证在这两个上下文中计算为相同的值.
Run Code Online (Sandbox Code Playgroud)#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预处理器(转换阶段 1到4)和"编译器正确"(阶段5到7)是两个独立的程序,#if表达式是只有预处理器必须知道执行字符集的地方.因此,通过使实现定义预处理器使用的"执行字符集"是否与编译器正确使用的相匹配,标准许可预处理器在源字符集中完成所有工作,使生活更容易一些在1989年.
说了这么多,我会非常惊讶地发现一个现代编译器,即使执行和源字符集严重不兼容,也不会使两个表达式都评估为相同的值.现代编译器倾向于集成预处理器 - 阶段1到7都由同一个程序执行 - 即使它们不执行,专门预处理器将其执行字符集与编译器本身匹配的工程负担也是微不足道的如今.
klu*_*utt 16
因为并非所有计算机都使用ascii或unicode.
在过去,一种叫做ebcdic的标准很常见.在ebcdic 500中,值为'z'169,值为'a'130.然后表达式'z'-'a'将计算为39.
这解释了为什么你不能为类型的表达式'a'或甚至假设某个值'z'-'a'.但是,它没有解释为什么Q中的两个表达式不能保证相等.
预处理器和编译器是两个不同的东西.预处理器处理源代码中使用的编码,而编译器则针对您正在编译的机器.有关更详细的解释,请参阅zwol的答案.
为了扩展其他正确的答案,仍在使用的非ASCII C编译器的真实示例是IBM的z/OS XL C/C++.默认情况下,它假定源文件位于IBM代码页1047(EBCDIC的版本与Latin-1具有相同的库)中.但是,它有几种不同的编译器选项,不仅支持ASCII,还支持"混合代码",或包含多个编码数据的源文件.(这些程序的存在是因为MVS编译器要求语法语句仅采用IBM-1047编码.)
从文档中可以看出,有可能使用这样的命令#pragma CONVLIT(suspend),这样可以使这两个语句在该编译器上进行不同的评估.我没有副本来测试MCVE.