Подтвердить что ты не робот

Почему "не все пути управления возвращают значение" является предупреждением, а не ошибкой?

Я пытался ответить на этот вопрос. Как было предложено принятым ответом, проблема с этим кодом заключается в том, что не все пути управления возвращают значение. Я пробовал этот код в компиляторе VC9, и он дал мне предупреждение о том же. Мой вопрос - почему это всего лишь предупреждение, а не ошибка? Кроме того, если путь, который не возвращает значение, выполняется, что будет возвращено функцией (он должен что-то вернуть)? Это все, что есть на вершине стека, или это страшное поведение undefined снова?

4b9b3361

Ответ 1

Невозможность вернуть значение из функции с типом возврата не void приводит к поведению undefined, но не является семантической ошибкой.

Причина этого, насколько я могу судить, в значительной степени историческая.

C изначально не имел void, а неявный int означал, что большинство функций возвратили int, если явно не объявлено о возврате чего-то еще, даже если не было намерения использовать возвращаемое значение.

Это означает, что многие функции возвратили int, но без явной установки возвращаемого значения, но это было ОК, потому что вызывающие абоненты никогда не использовали бы возвращаемое значение для этих функций.

Некоторые функции действительно возвращали значение, но использовали неявный int, потому что int был подходящим типом возврата.

Это означает, что в коде pre- void было много функций, которые номинально вернули int, но которые могли быть объявлены как возвращаемые void и множество других функций, которые должны возвращать int без четкого способа сказать разница. Принуждение return ко всем путям кодов всех не void функций на любом этапе приведет к поломке устаревшего кода.

Существует также аргумент о том, что некоторые пути кода в функции могут быть недоступными, но это может быть нелегко определить из простого статического анализа, поэтому зачем принудительно использовать ненужный return?

Ответ 2

Я бы предположил, что это только предупреждение, потому что компилятор не всегда может быть на 100% уверен, что можно не нанести ответ.

то есть. если у вас есть:


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

-= source2.c =-
int doSomething()
{
    return 1;
}

Компилятор в этом случае, возможно, не сможет знать, что он всегда ударит по возврату, но вы это сделаете. Конечно, это ужасная практика программирования, чтобы полагаться на знание того, как работает внешний код.

Что касается того, что действительно будет возвращено, это зависит от платформы. В x86 ABI EAX используется для возвращаемого значения (до 32 бит), поэтому он будет возвращать то, что когда-либо было помещено в этот регистр (что может быть возвратом от чего-то другого, временного значения или полного мусора).

Ответ 3

Технически не гарантируется ошибка, если вы вызываете функцию, и эта функция всегда выдает исключение. Например, вот какой-то псевдокод, и вы знаете, что raiseError всегда бросает.

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

Если компилятор не может увидеть реализацию raiseError, он не будет знать, что функция собирается бросить. Так что действительно нет никакого поведения undefined здесь. Конечно, это хорошо, чтобы заставить замолчать компилятор здесь, что вы можете сделать с написанием оператора return "dummy" после raiseError или фиктивного "броска". Я называю их "dummy", потому что они никогда не будут достигнуты в действительности. (Вы также можете подавить предупреждение, если вы действительно настаиваете). Однако нет ошибки или поведения undefined.

Ответ 4

вот еще одна причина, по которой это не ошибка

следующее сообщение даст вам то же предупреждение, поскольку компилятор ожидает, что вы вернете что-то из блока 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

Другой пример, когда некоторые пути управления могут быть неактуальны, чтобы не возвращать значение:

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

Это не ошибка, потому что это может быть предполагаемое поведение. Например, некоторые библиотеки шифрования используют неинициализированные локальные данные в качестве шага для посева. Поскольку возвращаемые значения хранятся в местах назначения вызовов и на платформе, это может помочь в некоторых необычных ситуациях (например, выше). В этом случае функция возвращает все, что осталось в регистре, используемом для возврата возвращаемого значения.

Ответ 7

Рассмотрим следующий сценарий:

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
}

С++ complier должен предоставить вам вашу "Матрицу"; Таким образом, это не ошибка.