我试图回答这个问题。正如接受的答案所建议的那样,该代码的问题在于并非所有控制路径都返回一个值。我在 VC9 编译器上尝试了这段代码,它给了我同样的警告。我的问题是为什么只是警告而不是错误?另外,如果不返回值的路径被执行,函数将返回什么(它必须返回一些东西)?它只是堆栈顶部的任何东西,还是再次出现可怕的未定义行为?
7 回答
无法从具有非void
返回类型的函数返回值会导致未定义的行为,但这不是语义错误。
据我所知,其原因主要是历史原因。
C 最初没有,void
并且隐式int
意味着大多数函数返回一个int
除非明确声明返回其他内容,即使不打算使用返回值。
这意味着许多函数返回一个 int 但没有显式设置返回值,但这没关系,因为调用者永远不会为这些函数使用返回值。
一些函数确实返回了一个值,但使用了隐式int
,因为int
它是一个合适的返回类型。
这意味着预void
代码有许多名义上返回int
但可以声明为返回void
的函数,以及许多其他应该返回的函数,但int
没有明确的方法来区分它们。return
在任何阶段对所有非函数的所有代码路径强制执行void
都会破坏遗留代码。
还有一个论点是函数中的某些代码路径可能无法访问,但这可能不容易从简单的静态分析中确定,那么为什么要强制执行不必要的return
.
我猜这只是一个警告,因为编译器不能总是 100% 确定可能不会返回。
即如果你有:
-= source1.c =-
int func()
{
if(doSomething())
{
return 0;
}
}
-= source2.c =-
int doSomething()
{
return 1;
}
在这种情况下,编译器可能无法知道它总是会返回,但你知道。当然,依靠了解外部代码的工作原理,这是一种糟糕的编程实践。
至于实际返回什么取决于平台。在 x86 ABI 上,EAX 用于返回值(最多 32 位),因此它将返回曾经放置在该寄存器中的内容(可能是来自其他东西的返回、临时值或总垃圾)。
从技术上讲,如果您调用一个函数并且该函数总是抛出异常,则不能保证是错误的。例如这里有一些伪代码,你知道 raiseError 总是抛出。
MyClass func( params )
{
if( allIsValid() )
{
return myObject;
}
else
{
raiseError( errorInfo );
}
}
如果编译器看不到 raiseError 的实现,它就不会知道该函数将要抛出。所以实际上这里实际上没有未定义的行为。当然,在这里使编译器保持沉默是很好的,您可以通过在 raiseError 之后编写“虚拟”返回语句或虚拟“抛出”来做到这一点。我称它们为“哑巴”,因为它们在现实中永远无法触及。(如果你真的坚持,你也可以压制警告)。但是没有错误或未定义的行为。
这是它不是错误的另一个原因
以下内容会给你同样的警告,因为编译器希望你从 catch 块中返回一些东西,即使你扔在那里
int foo(){
try{
return bar(0);
} catch(std::exception& ex){
//do cleanup
throw ex;
}
}
int bar(unsigned int i){
if(i == 0){
throw std::string("Value must be greater than 0");
} else{
return 0;
}
}
另一个示例,某些控制路径不返回值可能是可以的:
enum E : int {A, B};
int foo(E e) {
switch (e) {
case A: return 30;
case B: return 50;
}
}
有可能e
不是A
or B
,但这意味着它始终是这些值之一。如果是这样,那么代码很好,没有问题。将此警告变为强制性错误将需要不必要的“无法访问”的混乱。
如果您希望警告仍然是错误,您可以配置您的编译器以使用 /WX 或 -Werror 之类的标志来执行此操作。当然,您应该注意,不同的编译器可能会对无法访问的内容做出不同的决定,因此您可能会为不同的编译器修复不同的东西。
这不是错误,因为它可能是预期的行为。例如,一些加密库使用未初始化的本地数据作为种子的步骤。由于返回值保存在调用约定和平台特定的位置,这可能有助于一些不寻常的(如上述)情况。在这种情况下,该函数返回用于返回返回值的寄存器中剩余的任何内容。
考虑以下场景:
UINT GenderID(GENDER gender)
{
switch(gender)
{
case MALE:
return MALE_ID;
case FEMALE:
return FEMALE_ID;
}
// default not required because [GENDER] in our 'Matrix' CAN be either M or F
}
C++ 编译器应该让您拥有自己的“矩阵”;因此它不是错误。