Если функция не возвращает значения с допустимым типом возврата, можно ли компилятору выбрасывать мусор?

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

Например,

int func1() {
    return; // error
}

int func2() {
    // does not return anything
}

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

  int i;  // error
  int i = 6;  // okay

Есть мысли или это дублирующий вопрос? Я ценю вашу помощь.

4 ответа

Решение

В C++ такой код имеет неопределенное поведение:

[stmt.return] / 2... Выход из конца функции эквивалентен возврату без значения; это приводит к неопределенному поведению в функции, возвращающей значение....

Большинство компиляторов выдают предупреждение для кода, похожего на тот, что в вопросе.

Стандарт C++ не требует, чтобы это было ошибкой времени компиляции, потому что в общем случае было бы очень трудно правильно определить, действительно ли код запускается в конце функции, или если функция завершается через исключение (или longjmp или аналогичный механизм).

Рассматривать

int func3() {
    func4();
}

Если func4() бросает, то этот код полностью в порядке. Компилятор может не увидеть определение func4() (из-за отдельной компиляции), и поэтому не может знать, скинет или нет.

Кроме того, даже если компилятор может доказать, что func4() не бросай, все равно придется доказать, что func3() фактически вызывается до того, как он сможет законно отклонить программу. Такой анализ требует проверки всей программы, которая несовместима с отдельной компиляцией и которая даже не возможна в общем случае.

В С, цитируя N1256 6.9.1p12:

Если }, завершающий функцию, достигнут, и вызывающая сторона использует значение вызова функции, поведение не определено.

Таким образом, законно (но это плохая идея) для не-void-функции не возвращать значение, но если это происходит, и вызывающая сторона пытается использовать результат, поведение не определено. Обратите внимание, что он не обязательно просто возвращает произвольное значение; что касается стандарта, все возможно.

До ANSI C не было void ключевое слово, поэтому способ написания функции, которая не возвращала значение, состоял в том, чтобы пропустить возвращаемый тип, сделав его неявным возвращением int, Требуя return оператор в функции, возвращающей значение, сломал бы старый код. Это также потребовало бы дополнительного анализа компилятором, чтобы определить, что все пути кода достигают return заявление; такой анализ является разумным для современных компиляторов, но, возможно, был чрезмерным бременем, когда C был впервые стандартизирован.

C++ немного более строгий. В C++:

Выпуск из конца функции эквивалентен возврату без значения; это приводит к неопределенному поведению в функции, возвращающей значение.

поэтому поведение не определено, пытается ли вызывающая сторона использовать (несуществующий) результат или нет.

Компиляторы C и C++ наверняка могут предупредить об отсутствии return операторы или о путях управления, которые выпадают из конца функции без выполнения return заявление, но соответствующие стандарты не требуют их для этого.

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

С другой стороны return Оператор без выражения не может появляться в непустой функции.

Соответствующие части стандарта C99 - §6.9.1 для первого случая:

Если } которая завершает функцию, достигается, и значение вызова функции используется вызывающей стороной, поведение не определено.

и §6.8.6.4 для второго случая:

return оператор без выражения должен появляться только в функции, тип возвращаемой void,

Обе ваши функции плохо сформированы. Разница между ними в том, что ваш func1 нарушает правила о том, как return заявление может быть использовано в то время как ваш func2 является неопределенным поведением. return утверждение в вашем func1 является незаконным, и реализация должна диагностировать это. Отсутствие возврата в вашем func2 является неопределенным поведением. Большинство компиляторов будут диагностировать это, но никто не должен.

Другие вопросы по тегам