切换情况下的变量:UB还是编译器错误?

Adr*_*thy 2 c++ gcc arduino switch-statement language-lawyer

我试图确定是调用未定义行为还是遇到编译器错误。

我正在开发一些代码,以解释通过串行连接从外部组件发送到Arduino的消息。这是我开始使用的成员函数的简化版本。[这些Serial.println命令相当于Arduino的printf调试。]

void decodeMessage() {
  switch (getType()) {
    case 0x3A:
      Serial.println("foo message");
      break;
    case 0x3B:
      Serial.println("bar message");
      break;
    case 0x3C:
      Serial.println("zerz message");
      break;
    ... // and so on for 0x3D through 0x40
    case 0x41:
      Serial.println("ack message");
      break;
    default:
      Serial.println("unknown message type");
      break;
  }
}
Run Code Online (Sandbox Code Playgroud)

这对于所有消息类型均适用。然后,我修改了0x3B的大小写,以也检查消息参数中的一些位:

    case 0x3B:
      Serial.println("bar message");
      const auto mask = getParam();
      if (mask & 0x01) Serial.println("bit 0 set");
      if (mask & 0x02) Serial.println("bit 1 set");
      break;
Run Code Online (Sandbox Code Playgroud)

用此代码代替原始的0x3B情况,最后一个消息类型(0x41,“ ack”),其他所有东西都起作用。好像那案子的尸体不见了。默认情况继续有效,从0x3A到0x40也是如此。

经过尝试找出问题原因的许多尝试之后,我意识到我mask在开关中间引入了const变量(),而没有将其限定在特定情况下。当我添加括号时,它再次适用于所有情况:

    case 0x3B: {
      Serial.println("bar message");
      const auto mask = getParam();
      if (mask & 0x01) Serial.println("bit 0 set");
      if (mask & 0x02) Serial.println("bit 1 set");
      break;
    }  // braces to limit scope of `mask`
Run Code Online (Sandbox Code Playgroud)

问题:

  • 损坏的版本是否引起未定义的行为,或者这是编译器错误?如果是UB,我应该重新阅读规范的哪一部分?

  • 当您在开关盒中引入变量而不限制其范围时,我使用过的其他编译器(例如VC ++)会发出警告。是否可以从gcc(Arduino IDE使用的编译器)获得类似警告的选项?

Mic*_*zel 7

我认为此代码应该基于[stmt.dcl] / 3格式错误

可以转移到一个块中,但不能以一种绕过初始化声明的方式(包括条件和初始化语句中的声明)的方式进行转移。除非变量具有空位初始化([dcl.init]),否则从具有自动存储持续时间的变量不在范围内的点跳转到其处于范围内的点的程序是错误的。

强调我的。您的变量mask没有空虚初始化。至少就我的理解而言,“格式错误”隐式地需要诊断,即符合标准的编译器必须产生错误消息。因此,没有未定义的行为,就应该永远不要编译。

因此,我想说的是,这里缺乏诊断程序绝对可以视为编译器错误。但是请注意,没有一个可以尝试使用Godbolt的GCC版本可以接受此代码(而且可以追溯到很久以前)。如果Arduino IDE确实确实编译了这段代码而没有退缩,那么看来它必须使用一个绝望地过时/损坏的GCC版本…

适当的编译器对此抱怨的示例

要解决此问题,只需将变量包装在块作用域中,这样就不会有可能的控制流进入变量声明的作用域,而无需通过它的声明,就象您已经发现自己一样。例如,把这个

void f(int x)
{
    switch (x)
    {
    case 1:
        const int y = 42;  // error
        break;

    case 2:
        break;
    }
}
Run Code Online (Sandbox Code Playgroud)

进入

void f(int x)
{
    switch (x)
    {
    case 1:
        {
            const int y = 42;  // OK
            break;
        }

    case 2:
        break;
    }
}
Run Code Online (Sandbox Code Playgroud)