用于switch-case表达式比较的促进类型是什么?

4
以下程序在不同编译器上编译时会打印出"unknown"。为什么会这样呢?
#include "stdio.h"

const char OPTION = (char)(unsigned char)253;

int main(int argc, char* argv[])
{
    unsigned char c = 253;
    switch (c)
    {
    case OPTION:
        printf("option\n");
        break;
    default:
        printf("unknown\n");
        break;
    }

    return 0;
}

当查看C++标准(N3690 2013-05-05)时,我看到了一个关于switch的条款:

6.4.2 The switch statement

2 The condition shall be of integral type, enumeration type, or class type. If of class type, the condition is contextually implicitly converted (Clause 4) to an integral or enumeration type. Integral promotions are performed. Any statement within the switch statement can be labeled with one or more case labels as follows:

case constant-expression :  

where the constant-expression shall be a converted constant expression (5.19) of the promoted type of the switch condition. No two of the case constants in the same switch shall have the same value after conversion to the promoted type of the switch condition.

所引用的转换条款:

4 标准转换

2 [ 注:在多种情况下,具有给定类型的表达式将被隐式转换为其他类型:
[...]
— 在 switch 语句的表达式中使用时。目标类型是整数型(6.4)。
[...]
—注释结束]

变量 c 的类型是无符号字符型,它是一种整数类型。因此不需要进行提升!?

如果提升类型是 unsigned char,我会期望像 c == (unsigned char)OPTION 这样的比较结果是 true。如果提升类型是 int,我会期望像 (int)c == (int)OPTION 这样的比较结果显然是 false。

我的问题是:上面的程序中使用了哪种提升类型?C 和 C++ 标准中的相关条款是什么?


1
在C99中,这是无效的,因为OPTION不是整数常量(不能用作switch标签)。 - mafso
如果你用(char)(unsigned char)253替换case OPTION(如果char是有符号的话可能未定义),那么在C99/C11中,所有东西都会被提升为int(只要unsigned char总是适合于int,这对于每个平台来说都是成立的,我想)。整数提升在C99/C11 §6.3.1.1(2)中有定义;§6.8.4.2“switch语句”也可能相关。 - mafso
3个回答

6

涉及哪些类型?

在下面的章节中,推广的类型将是int:

4.5p1整数提升[conv.prom]

除了bool,char16_t,char32_twchar_t之外的整数类型的prvalue,其整数转换等级(4.13)小于int的等级,如果int可以表示源类型的所有值,则可以将其转换为int类型的prvalue;否则,源prvalue可以转换为类型为unsigned int的prvalue。


为什么代码在不同平台上表现不同?

是否有符号或无符号是实现定义的,如标准中所述:

3.9.1p1基本类型[basic.fundamental]

实现定义是否char可以保存负值。字符可以明确声明为signedunsigned

...

在任何特定的实现中,一个普通的char对象可以具有与signed charunsigned char相同的值;哪一个是实现定义的。


这与什么有关?

前面引用的章节意味着下面一行代码中的char类型转换不一定会产生253的值。

const char OPTION = (char)(unsigned char)253;

如果在一个8位的平台上,char 被设置为可以存储负值,那么 253 将无法被存储,并且在初始化后,OPTION 的值很可能会变成 -3

换句话说...

你的帖子中,经过整数提升后的开关在语义上等同于下面的if-else语句,因为我们有一个条件和一个默认情况。
unsigned char c = 253;

//   .---------.-------------------- integral promotion
//   v         v
if ((int)c == (int)OPTION) {
  printf ("OPTION\n");
} else {
  printf ("DEFAULT\n");
}

取决于底层实现,OPTION 可能等于 253 或者 -3;这将产生你所描述的行为。


注意:本帖中所有标准引用来自最终的 C++11 标准(草案)n3337


那么在4.5p1中,“can”不应该被理解为可选的晋升吗?但是它必须被理解为“unsigned char必须被晋升,并且在4.5的列表中只列出了int作为它可以晋升的类型”!? - Werner Henze
@WernerHenze,“can”指的是在需要的上下文中“将会”,正如[expr]p9所述。 - Filip Roséen - refp

1
这里涉及到的是“整数提升”的部分。
简而言之,小于int类型的值被提升为int类型(如果int无法表示所有值,则提升为unsigned int类型)。
所以,您将 c 提升为int,它的值为253。同时, OPTION 的值为-3,也被提升为int,其值为-3。(char的符号取决于平台,因此该程序在不同平台上的行为可能会有所不同。char可以容纳的值的范围也取决于平台,尽管在具有8位有符号char的2s补码平台上进行253到-3的转换是常见的。)

0
正如您引用的语录所说:“执行积分促销”。因此,在这个表达式中。
switch (c)

c会被转换为int类型,并且其值为253,因为c是一个无符号整数对象。

在这个标签中

case OPTION:

由于 OPTION 是有符号字符(我认为默认情况下 char 表现为有符号字符),因此符号位将被传播。

因此,控制将传递到标签 default,因为 (unsigned char)253 的 (int) 值不等于 (signed char)253 的 (int) 值。


我理解“积分提升”规则是“类型x 可以 转换为类型y”,而不是严格的规定类型x必须提升为类型y!? - Werner Henze
@Werner Henze 标准是以一种常规程序员难以理解的方式编写的。:) 这是一项业务。 :) - Vlad from Moscow

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接