代码之家  ›  专栏  ›  技术社区  ›  Naveen

为什么“并非所有控制路径都返回值”是警告而不是错误?

  •  28
  • Naveen  · 技术社区  · 15 年前

    我想回答 this 问题。正如被接受的答案所建议的,该代码的问题在于并非所有的控制路径都返回一个值。我在VC9编译器上试过这段代码,它警告了我这段代码。我的问题是,为什么只是一个警告而不是一个错误?另外,如果执行了不返回值的路径,那么函数将返回什么(它必须返回一些东西)?它到底是栈顶上的什么东西,还是又是可怕的未定义行为?

    7 回复  |  直到 9 年前
        1
  •  30
  •   CB Bailey    15 年前

    未能从具有非- void 返回类型导致未定义的行为,但不是语义错误。

    据我所知,这主要是因为历史原因。

    C原来没有 无效 和隐含的 int 意味着大多数函数返回 int 除非明确声明返回其他内容,即使不打算使用返回值。

    这意味着许多函数返回了一个int,但没有显式地设置返回值,但这是可以的,因为调用方永远不会为这些函数使用返回值。

    一些函数确实返回了一个值,但使用了隐式 int 因为 int 是一个合适的返回类型。

    这意味着- 无效 代码具有许多名义上返回的函数 int 但可以宣布归还 无效 还有许多其他函数应该返回 int 没有明确的方法来区分。强制执行 return 在所有非- 无效 任何阶段的函数都会破坏遗留代码。

    还有一个论点是,函数中的某些代码路径可能无法访问,但这可能不容易通过简单的静态分析来确定,所以为什么要强制执行不必要的 返回 ?

        2
  •  9
  •   tyranid    15 年前

    我猜这只是一个警告,因为编译器不能总是100%确定是否可能不返回。

    也就是说,如果你有:

    
    -= source1.c =-
    int func()
    {
        if(doSomething())
        {
           return 0;
        }
    }
    
    -= source2.c =-
    int doSomething()
    {
        return 1;
    }
    

    在这种情况下,编译器可能无法知道它总是命中返回值,但确实如此。当然,依赖于了解外部代码如何工作是很糟糕的编程实践。

    至于实际返回的内容,取决于平台。在x86上,abis eax用于返回值(最多32位),因此它将返回放在该寄存器中的内容(可能是来自其他对象的返回,临时值或总垃圾)。

        3
  •  6
  •   CashCow    12 年前

    从技术上讲,如果调用一个函数,并且该函数总是抛出一个异常,则不能保证它是一个错误。例如,这里有一些伪代码,你知道raiserror总是抛出。

    MyClass func( params )
    {
         if( allIsValid() )
         {
           return myObject;
         }
         else
         {
            raiseError( errorInfo );
         }
    }
    

    如果编译器看不到raiseError的实现,它将不知道函数将抛出。所以实际上这里没有不明确的行为。当然,最好让这里的编译器保持安静,您可以在raiserror之后编写一个“dummy”返回语句,也可以编写一个“throw”伪语句。我称他们为“笨蛋”,因为他们在现实中永远不会被联系到。(如果您真的坚持,也可以取消警告)。但是,没有错误或未定义的行为。

        4
  •  1
  •   FearlessHyena    12 年前

    这是另一个原因,它不是一个错误

    下面将给出相同的警告,因为编译器希望您从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;
       }
    }
    
        5
  •  1
  •   bames53    9 年前

    另一个例子是,某些控制路径可能不返回值:

    enum E : int {A, B};
    
    int foo(E e) {
      switch (e) {
        case A: return 30;
        case B: return 50;
      }
    }
    

    有可能 e 不会 A B 但这意味着它将永远是这些价值观中的一个。如果是这样的话,代码就可以了,没有问题。将此警告设置为强制错误需要不必要的“无法到达”混乱。

    如果您希望警告仍然是一个错误,您可以使用/wx或-werror这样的标志配置编译器。当然,您应该注意到,不同的编译器可能会对无法访问的内容做出不同的决定,因此您可能会为不同的编译器修复不同的内容。

        6
  •  -2
  •   Malkocoglu    15 年前

    这不是一个错误,因为它可能是预期的行为。例如,一些加密库使用未初始化的本地数据作为种子设定的步骤。由于返回值保存在调用约定和特定于平台的位置中,这可能有助于在某些异常情况下(如上述情况)。在这种情况下,函数返回寄存器上用于返回返回值的任何内容。

        7
  •  -2
  •   Ujjwal Singh mattalxndr    12 年前

    考虑以下情况:

    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++编译器 让你 把你的“矩阵”按你的方式排列,这样就不是错误了。