我正在尝试清理一些 C 代码并构建一个 Debian 软件包。 makefile 设置为使用 -Wall 但 debuild 使用 -Wpedantic 。
碰巧这是“一件好事”,因为代码将数据指针转换为函数指针(ISO C 中不允许并且非常危险......在某些体系结构上数据和代码可能位于不同的地址空间)
然而,代码还在 switch 语句中使用范围,这些是 gcc(和 clang)扩展,因此 -Wpedantic 会出现问题。
case QNAP_PICSTS_SYS_TEMP_0 ... QNAP_PICSTS_SYS_TEMP_70:
(#define 不是枚举)
我收集开关范围的使用在内核中很常见,我可以重新编码它以使用带有一些 if/else 逻辑的默认值,但这实际上会更慢(跳转表与代码)
那么 1 是否有一个选项只允许此功能,2(为了奖励积分)我如何在 Debian 软件包规则中设置它?
选项在这里:
http://gcc.gnu.org/onlinedocs/gcc-4.1.2/gcc/Warning-Options.html
答案1
我查看了使用大范围的 switch/case/default 的编译器输出;我将它与 if/else if/else 中实现的相同代码进行了比较(源代码)。
除了名称之外,它产生完全相同的二进制代码(Godbolt 的编译器浏览器),当与 debian 12 中默认的 GCC 12.2 一起使用时,以及与dpkg-buildflags
debian 使用的相关内容一起使用时。
幸运的是,现代编译器可以看到简单的内容if(state == constant) expr; else if(state == other_constant)…
,如果它们很短,就会将它们转换为跳转表。因此,代码与 switch/case 完全相同。这已经发生在-O1
,所以这不是一些危险的优化。
而且,更令人惊讶的是,诸如 get 之类的结构case 5 ... 74:
会转换为与 75 的比较(在跳转表已经处理了低于 5 的情况之后)。
所以,实际上,在现代代码中没有理由使用switch
and case
,除非您认为它case CONSTANT: … break;
看起来比 更干净if(state == CONSTANT){ … }
。我不知道 - 我也不认为你的编译器会这样做:使用块可以实现更严格的作用域。
有一个例外,那就是当然case:
withoutbreak
允许您基本上模拟goto
without goto
:
switch(state) {
case 1:
value += 0.2f;
case 2:
retval = 1.0f/value;
break
// …
}
相当于假设的
if (state == 1) {
value += 0.2f;
goto label_in_two;
}
else if(state == 2)
label_in_two:
retval = 1.0f/value;
}
(我们都知道忘记 will 带来的错误;如果你不这样做,break;
使用 will 会带来同样的问题goto
非常小心。)
我希望你明白switch
对于构建有限状态机的人来说,这确实是一种方便的构造,而不是与范围一起使用 - 与不古老的编译器相比,根本没有任何优势,if/else if
而且在不明显的控制流中存在大量错误来源。
因此,我认为,事后看来,现代编译器优化的知识很好,我认为 GNU 的case
范围特性是考虑不周的,因此警告是明智的。
因此,我得出的结论是,消除该警告的正确方法是将//switch
转换为//构造。case
default
if
else if
else