C++ 为什么;并非所有控制路径都返回一个值";警告不是错误吗?
我试图回答这个问题。正如公认的答案所建议的,该代码的问题在于并非所有控制路径都返回值。我在VC9编译器上尝试了这段代码,它给了我同样的警告。我的问题是为什么这只是一个警告而不是一个错误?另外,如果执行了不返回值的路径,函数将返回什么(它必须返回什么)?它只是堆栈顶部的东西,还是可怕的未定义行为再次出现?这不是错误,因为它可能是预期的行为。例如,某些加密库使用未初始化的本地数据作为种子设定的步骤。由于返回值保存在调用约定和特定于平台的位置,因此在某些异常(如上述)情况下可能会有所帮助。在这种情况下,函数将返回用于返回返回值的寄存器上剩余的任何内容。我猜这只是一个警告,因为编译器不能始终100%确定不可能返回 i、 e.如果你有:C++ 为什么;并非所有控制路径都返回一个值";警告不是错误吗?,c++,compiler-warnings,C++,Compiler Warnings,我试图回答这个问题。正如公认的答案所建议的,该代码的问题在于并非所有控制路径都返回值。我在VC9编译器上尝试了这段代码,它给了我同样的警告。我的问题是为什么这只是一个警告而不是一个错误?另外,如果执行了不返回值的路径,函数将返回什么(它必须返回什么)?它只是堆栈顶部的东西,还是可怕的未定义行为再次出现?这不是错误,因为它可能是预期的行为。例如,某些加密库使用未初始化的本地数据作为种子设定的步骤。由于返回值保存在调用约定和特定于平台的位置,因此在某些异常(如上述)情况下可能会有所帮助。在这种情况下
-= source1.c =-
int func()
{
if(doSomething())
{
return 0;
}
}
-= source2.c =-
int doSomething()
{
return 1;
}
在本例中,编译器可能无法知道它将始终返回,但您确实知道。当然,依靠了解外部代码是如何工作的,这是一种糟糕的编程实践
至于实际返回的内容,则取决于平台。在x86上,ABIs EAX用于返回值(最多32位),因此它将返回该寄存器中曾经放置的内容(可能是来自其他内容的返回,临时值或总垃圾)。如果无法从具有非
void
返回类型的函数返回值,则会导致未定义的行为,但这不是语义错误
据我所知,其原因主要是历史原因
C最初没有void
,隐式int
意味着大多数函数返回int
,除非明确声明返回其他值,即使无意使用返回值
这意味着许多函数返回int,但没有显式设置返回值,但这是可以的,因为调用者永远不会为这些函数使用返回值
有些函数确实返回值,但使用隐式int
,因为int
是一种合适的返回类型
这意味着pre-void
code有许多函数名义上返回int
,但可以声明为返回void
,还有许多其他函数应该返回int
,但没有明确的区别。在任何阶段对所有非void
函数的所有代码路径强制执行return
,都会破坏遗留代码
还有一种观点认为,函数中的某些代码路径可能无法访问,但这可能不容易从简单的静态分析中确定,因此为什么要强制执行不必要的
返回?,这是它不是错误的另一个原因
下面将给出相同的警告,因为编译器希望您从catch块返回一些内容,即使您正在抛出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;
}
}
从技术上讲,如果调用一个函数并且该函数总是抛出异常,则不能保证该函数是错误的。例如,这里有一些伪代码,您知道raiseError总是抛出
MyClass func( params )
{
if( allIsValid() )
{
return myObject;
}
else
{
raiseError( errorInfo );
}
}
如果编译器看不到raiseError的实现,它将不知道函数将抛出。所以这里实际上没有未定义的行为。当然,在这里让编译器静音是很好的,您可以在raiseError之后编写一个“dummy”返回语句,或者编写一个伪“throw”。我称他们为“傻瓜”,因为他们在现实中永远无法触及。(如果您确实坚持,也可以取消显示警告)。但是,没有错误或未定义的行为。请考虑以下情况:
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++编译器,应该<强>让你你的矩阵“你的方式”;因此,这不是一个错误。
另一个示例,在该示例中,某些控制路径可能不返回值:
enum E : int {A, B};
int foo(E e) {
switch (e) {
case A: return 30;
case B: return 50;
}
}
e
可能不是A
或B
,但这意味着它总是这些值之一。如果是这种情况,那么代码是好的,没有问题。将此警告变成强制错误将需要不必要的“无法到达”混乱
如果您仍然希望警告是错误,那么可以使用/WX或-Werror这样的标志来配置编译器。当然,您应该注意,不同的编译器可能会对无法访问的内容做出不同的决定,因此您可能会为不同的编译器修复不同的内容。您能否举一个使用未初始化数据作为种子的加密库的具体示例?听上去,一个有能力的库不太可能做到这一点,因为未初始化的数据在加密术语中是随机的。真的吗?什么加密库可以做到这一点?依赖于加密库中未定义的行为似乎是一个巨大的潜在安全漏洞。我希望有一天能使用正确生成的随机数,而不是未初始化的数据作为种子。如果您使用MSVC++调试器,您会经常看到未初始化的数据具有固定值(在程序0xCCCC、0xCDCDCDCD的调试版本中)。@Shailesh:我不喜欢未初始化的数据结构。我只记得,有一次,一些程序员依赖于这个东西…@Shailesh:不幸的是,很难正确地生成随机数()。我还记得一些密码学者通过使用芯片上的其他硬件(也许是声卡,我不记得)攻击英特尔硬件RNG,这样他就可以保持那个区域的温度足够高,RNG产生很少的熵(因为它依赖于热噪声)。