这个源代码在C中打开一个字符串.它是如何做到的?

Ian*_*ton 105 c label constants switch-statement

我正在阅读一些模拟器代码,我反驳了一些奇怪的东西:

switch (reg){
    case 'eax':
    /* and so on*/
}
Run Code Online (Sandbox Code Playgroud)

这怎么可能?我以为你只能switch在积分类型上.是否有一些宏观技巧在继续?

Bat*_*eba 145

(只有你可以回答"宏诡计"部分 - 除非你粘贴更多的代码.但是这里没有太多的宏可以继续工作 - 正式你不允许重新定义关键字 ;这样做的行为是未定义的.)

为了实现程序可读性,诙谐的开发人员正在利用实现定义的行为.'eax'不是一个字符串,而是一个多字符常量.请仔细注意周围的单引号字符eax.很可能它会int在你的情况下为你提供一个独特的角色组合.(通常每个字符占32位的8位int).每个人都知道,你可以switchint!

最后,标准参考:

C99标准说:

6.4.4.4p10:"包含多个字符(例如,'ab')的整数字符常量的值,或包含未映射到单字节执行字符的字符或转义序列的值是实现定义的. "

  • 为了防止任何人看到这种情况并发生恐慌,"实现定义"需要工作并由编译器以适当的方式记录(标准不要求行为直观或文档有任何好处,但是...).这对于完全理解他们正在写作的内容的编码人员来说是"安全的",而不是"未定义". (55认同)
  • @jpmc26这不是未定义的行为,它是实现定义的.因此,除非编译器文档提到恶魔,否则你的鼻子是安全的. (16认同)
  • @Justin尽管可能,但这将是非常不正常的.如果它没有做出答案所暗示的最有可能,那么下一个可能性就是它只使用第一个字符并忽略其余字符. (7认同)
  • @ZanLynx:恐怕最初的意图早于Unicode,UTF-8和任何多字节字符编码都要花费近20年的时间.*多字符常量*只是表示表示2,3或4字节组(取决于字节和int大小)的整数的简便方法.实现和体系结构不一致导致委员会将此声明为*实现定义*,这意味着没有可移植的方法来计算"a"和"b"的"ab"的值. (7认同)
  • @ZanLynx我不是肯定的,但我相信这个功能早于Unicode和其他MBCS标准.看起来像内存转储中的文本和RIFF样式的文件格式块ID的"幻数"是我所知道的第一个应用程序. (5认同)
  • 请注意:原始意图是针对像Unicode这样的多字节字符.屏幕上的一个UTF8"字符"最多可以包含四个字节. (4认同)
  • @Barmar有正式的"未定义"行为,允许编译器让恶魔飞出你的鼻子,到了你必须*不断*防止意外调用它,这开始是非常不正常的...... (3认同)
  • Apple补充说,很可能是因为他们的平台充满了用于识别数据类型和格式的"fourcc"代码.例如,文件类型.经常选择Fourcc代码,当解释为ASCII字节时拼写一些东西. (2认同)

Vla*_*cow 44

根据C标准(6.8.4.2开关声明)

3 每个case标签的表达式应为整数常量表达式 ...

和(6.6常量表达式)

6 整数常量表达式应具有整数类型,并且只能具有整数常量的操作数,枚举常量, 字符常量,结果为整数常量的sizeof表达式,以及作为强制转换的直接操作数的浮点常量.整数常量表达式中的转换运算符只能将算术类型转换为整数类型,除非作为sizeof运算符的操作数的一部分.

现在是什么'eax'

C标准(6.4.4.4字符常量)

2整数字符常量是用单引号括起来的一个或多个多字节字符的序列,如'x'中所示...

因此,'eax'根据同一部分的第10段,它是整数字符常量

  1. ...包含多个字符(例如,'ab')的整数字符常量的值,或包含未映射到单字节执行字符的字符或转义序列的值是实现定义的.

因此,根据第一个提到的引用,它可以是整数常量表达式的操作数,可以用作案例标签.

注意字符常量(用单引号括起来)具有类型,int并且与具有字符数组类型的字符串文字(用双引号括起来的字符序列)不同.


Sti*_*mer 12

正如其他人所说,这是一个int常数,其实际值是实现定义的.

我假设代码的其余部分看起来像

if (SOMETHING)
    reg='eax';
...
switch (reg){
    case 'eax':
    /* and so on*/
}
Run Code Online (Sandbox Code Playgroud)

您可以确定第一部分中的'eax'与第二部分中的'eax'具有相同的值,因此一切正常,对吧?......错了.

在评论中@Davislor列出了'eax'的一些可能值:

... 0x65,0x656178,0x65617800,0x786165,0x6165,或别的东西

注意第一个潜在价值?那就是'e',忽略其他两个角色.问题是程序可能使用'eax','ebx'等等.如果所有这些常量具有与'e'最终结果相同的值

switch (reg){
    case 'e':
       ...
    case 'e':
       ...
    ...
}
Run Code Online (Sandbox Code Playgroud)

这看起来不太好,是吗?

关于"实现定义"的好处是程序员可以检查他们的编译器的文档,看看它是否对这些常量做了明智的事情.如果有,家免费.

糟糕的是,其他一些可怜的家伙可以接受代码并尝试使用其他编译器进行编译.即时编译错误.该程序不可移植.

正如@zwol在评论中指出的那样,情况并不像我想象的那么糟糕,在不好的情况下代码无法编译.这至少会为您提供问题的确切文件名和行号.不过,你还没有工作计划.

  • 具有相同值的两个案例标签是约束违规(6.8.4.2p3:"......在同一个switch语句中没有两个案例常量表达式在转换后应具有相同的值")所以,只要所有代码将这些常量的值视为不透明,这可以保证工作或无法编译. (6认同)