2

如果我希望switch(an_enum)在错过枚举案例时报告我的语句,我可以打开-Wswitch编译器标志(在 gcc 上)。

enum E { e1, e2, e3 };

...
switch(e) {
  case e1: ...
  case e2: ...
  // NO default: checked by -Wswitch and -Werror
}

它工作得很好:“错误:枚举值'e3'未在开关[-Werror = switch]中处理”

但是现在我的代码的正确性取决于使用的编译器标志,这有点脆弱。

像这样的东西:

switch(e) __attribute__((exhaustive))
{
  ...
}

-Wswitch如果标志关闭,有没有办法让那段代码失败?还是在代码中临时打开它?

4

2 回答 2

4

可靠且便携的解决方案是在您的声明中添加default标签。switch它可以做到:

default:
    assert(0);
    abort();

这样即使断言被禁止,它也会失败——或者如果你愿意,你可以做一些更明智的事情。

如果您使用的是 GCC,则可以使用诊断编译指示-Wswitch来获得在短范围内打开所需的结果。

#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic error "-Wswitch"
#endif /* __GNUC__ */

switch (e)
{
…
}

#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif /* __GNUC__ */

这会推送诊断状态,将其更改为添加错误,就像-Wswitch在命令行上指定的一样,编译switch语句,然后弹出(恢复)保存的诊断状态。

如果您从不使用 GCC(或者可能是 Clang)以外的编译器,则可以省略__GNUC__测试。然而,其他平台上的编译器(例如 AIX 7.2 上的 IBM XLC 13.1.3)抱怨 GCC 特定的编译指示,尽管C 标准说它不应该这样做。您付钱并选择。

如果您愿意,可以使用ignoreorwarning代替error来实现其他效果。

您可能还注意到该-Wswitch选项是启用的警告之一-Wall— 如果您使用 GCC 进行编译,则应始终使用-Wall(and -Wextraand -Werror),因此您永远不会遇到问题。

此外,程序的正确性取决于您编写(或修改)的方式,而不是编译器选项。取决于编译器选项的是编译器是否发现了您所做的错误。

于 2019-08-21T07:06:58.220 回答
0

假设枚举仅包含示例中的相邻数字,那么您可以通过将 switch 语句替换为函数指针表来“展开”switch语句,这是相当普遍的做法。函数指针表将是纯标准 C。示例:

typedef enum 
{ 
  e1, 
  e2, 
  e3,
  eN  // enum counter
} e_t;

typedef void e_func_t (void);

e_func_t* const do_stuff [] =   // array of functions of type void f(void);
{
  e1_stuff, 
  e2_stuff,
  e3_stuff,
};

// force compilation error if missing a "case statement":
_Static_assert(sizeof(do_stuff)/sizeof(*do_stuff) == eN, 
               "Function pointer table incomplete.");

然后你可以用这个替换整个开关:

if(e < e1 || e >= eN)
{  
  /* handle errors or "default" here */ 
}
else
{
  do_stuff[e]();
}
于 2019-08-21T07:54:49.457 回答