GCC中enum switch控制流分析不足

发布于 2024-11-16 04:57:21 字数 660 浏览 11 评论 0原文

在以下 C++ 代码中:

typedef enum { a, b, c } Test;

int foo(Test test) {
    switch (test) {
        case a: return 0;
        case b: return 1;
        case c: return 0;
    }
}

使用 -Wall 编译时发出警告,表示控制到达非 void 函数的末尾。为什么?


编辑

示例中的变量 test 可以包含任何值通常是不正确的。

foo(12354) 无法编译:

> test.cpp:15:14: error: invalid conversion from ‘int’ to ‘Test’
> test.cpp:15:14: error:   initializing argument 1 of ‘int foo(Test)’

因为 12354 不是有效的 Test 值(尽管它确实在普通 有效) C,但不是 C++)。

您确实可以将任意整数常量显式转换为枚举类型,但这不是被视为未定义行为吗?

In the following C++ code:

typedef enum { a, b, c } Test;

int foo(Test test) {
    switch (test) {
        case a: return 0;
        case b: return 1;
        case c: return 0;
    }
}

a warning is issued when compiling with -Wall, saying that control reaches end of non-void function. Why?


Edit

Its not generally correct to say that the variable test in the example can contain any value.

foo(12354) does not compile:

> test.cpp:15:14: error: invalid conversion from ‘int’ to ‘Test’
> test.cpp:15:14: error:   initializing argument 1 of ‘int foo(Test)’

because 12354 isn't a valid Test value (though it indeed would be valid in plain C, but it's not in C++).

You sure could explicitly cast an arbitrary integer constant to the enum type, but isn't that considered Undefined Behaviour?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

风流物 2024-11-23 04:57:22

尽管向 foo 传递一个不会命中任何 return 语句的参数确实存在危险,但警告并不依赖于枚举或危险。 您可以在 switch 语句中使用 bool 看到相同的效果,据我所知,它是完全无懈可击的。

一般来说,编译器不够智能,无法推断出您是否已经覆盖了控件实际上可以通过 switch 语句采取的所有可能路径。为了变得如此聪明,它必须能够推断出程序在进入开关之前可以达到的所有可能状态,这直接导致了停止问题。

因此,推导必须在某个地方停止,并且(至少对于 gcc)它会在确定不存在默认情况时停止,因此控制可能能够离开开关 无需点击返回

Although there is actual danger of passing foo an argument that will not hit any of the return statements, the warning does not depend on enum, or on the danger. You can see the same effect with bool, in a switch statement which is (as far as I can tell) completely watertight.

In general the compiler isn't smart enough to deduce whether you've covered every possible path that control could actually take through the switch statement. To be that smart it would have to be able to deduce all of the possible states the program can reach before entering the switch, which leads straight to the Halting Problem.

So the deduction has to stop somewhere, and (at least with gcc) it stops with the determination that there is no default case and that therefore control might be able to leave the switch without hitting return.

弥繁 2024-11-23 04:57:22

无法保证变量 test 将包含有效的枚举,因此实际上您有可能到达非 void 函数的末尾,例如,如果您的调用代码如下所示:

Test test = Test(3);
foo(test);

There is no guarantee that the variable test will contain a valid enum so it is in fact possible for you to reach the end of your non-void function, e.g. if your calling code looks like this:

Test test = Test(3);
foo(test);
韶华倾负 2024-11-23 04:57:22

虽然您的枚举只有三个声明状态,但实现实际上会选择一个更大的整型(通常是 int)来存储值,这些值不限于声明的状态。该标准只是做出一些最低限度的保证,以确保它至少可以处理指定的值和某些组合。有时这种自由是必不可少的,因为枚举值旨在按位或组合在一起以形成组合。因此,实际上可以使用 Test(3) 来调用函数 foo,但在 Return 语句中找不到 return 语句>切换。

Although your enum has only three declared states, the implementation will actually choose a larger integral type (typically int) to store the values, which are NOT restricted to those declared. The Standard just makes some minimal guarantees to ensure it can at least handle the values specified and certain combinations. Sometimes this freedom is essential, as the enum values are intended to be bitwise-ORed together to form combinations. So, the function foo can actually be called with say, Test(3), which wouldn't find a return statement in your switch.

半仙 2024-11-23 04:57:21

问题是 Test 类型的变量可以具有编译器赋予它的类型允许的任何值。因此,如果它确定它是 32 位无符号整数,则允许该范围内的任何值。因此,如果您调用 foo(123456),您的 switch 语句将不会捕获任何值,并且 之后不会有 return开关。

在您的开关中放置一个 default 案例或添加一些错误处理代码。

The problem is that a variable of type Test can have any value allowed by the type the compiler gives it. So if it decided it was a 32-bit unsigned integer, any value in that range is allowed. So, if for instance you call foo(123456), your switch statement will not catch any value and there's no return after your switch.

Put a default case in your switch or add some error-handling code.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文