Если функция не возвращает значения с допустимым типом возврата, можно ли компилятору выбрасывать мусор?
Если функция имеет тип возврата, отличный от 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
является неопределенным поведением. Большинство компиляторов будут диагностировать это, но никто не должен.