Что (на самом деле) происходит, когда вызывается функция с предупреждением "control достигает конца не-void функции"?
Я знаю, что это означает, я просто задаюсь вопросом, почему это сообщение не, а просто предупреждение?
Что происходит в этом случае? Например, предположим, что у меня есть функция
int f()
{
}
и что происходит, когда я это называю?
Компилятор добавляет возвращение "неинициализированного" int
в этом случае?
Или недостающее возвращение может привести к повреждению стека?
Или это (абсолютно) undefined поведение?
Протестировано с помощью gcc 4.1.2 и 4.4.3
РЕДАКТИРОВАТЬ: Чтение ответов. Я понимаю одно, читаю комментарии - другое.
ОК, давайте суммируем: это поведение undefined. Тогда это означает, что можно привести к повреждению стека, не так ли? (это даже означает, что мой компьютер может начать бросать гнилые помидоры через меня через микрофонный домкрат, кричать - "что ты сделал???" ).
Но если это так, то почему в главном ответе здесь говорится, что коррупция стека не может произойти и, в то же время, что поведение undefined?
И undefined в отношении? Вызывающий, который пытается использовать "не возвращенное значение" или просто конец функции, является undefined, если он должен возвращать значение, но это не так?
Или это не undefined поведение, и только пользователь, который пытается использовать значение (которое не возвращается, d'oh!) будет "получать" undefined значение? Другими словами - просто какая-то ценность для мусора, и больше ничего не может произойти?
Ответы
Ответ 1
A: Нет, недостающее возвращение не приведет к повреждению стека
A: Да, поведение было бы "undefined", если вызывающий пользователь попытался прочитать и/или использовать возвращаемое значение (undefined!).
PS:
Здесь цитата для С++:
С++ 03 §6.6.3/2:
Оттекание конца функции эквивалентно возврату без стоимость; это приводит к поведению undefined при возврате значения функция.
Ответ 2
Вы спрашивали о C и С++. Правила на разных языках различны.
В C поведение undefined, только если вызывающий объект пытается использовать значение, возвращаемое функцией. Если у вас есть:
int func(void) {
/* no return statement */
}
...
func();
то поведение корректно определено.
В С++ поведение undefined (если функция вообще вызывается), пытается ли вызывающий объект использовать результат или нет. (Это по историческим причинам: перед ANSI C не было ключевого слова void
, а функции, которые не предназначались для возврата значения, обычно определялись (неявно) для возврата int
.)
Ответ Джона Боде уже цитировал проект N1570 стандарта ISO C, 6.9.1p12:
Если достигается }, которая завершает функцию, и значение вызов функции используется вызывающим, поведение undefined.
И paulsm4 привел стандарт С++; цитируя последнюю версию 2011 года, 6.6.3p2:
Вытекание конца функции эквивалентно return
без стоимость; это приводит к поведению undefined при возврате значения функция.
Исторические причины, приведшие к тому, что C разрешающая функция возврата значения не возвращает значение, если значение не используется вызывающим, не относится к С++, на дизайн которого не оказало сильное влияние необходимо избегать нарушения старого (pre-ANSI C) кода.
В обоих C (начиная с C99) и С++ функция main
является частным случаем; достижение }
main
без выполнения возврата эквивалентно a return 0;
. (C разрешает main
возвращать определенный для реализации тип, отличный от int
; в этом (редком) случае падение с конца возвращает неопределенный статус завершения в среду хоста.)
Конечно, исключение оператора return
из функции возврата значения или наличие возможного пути выполнения, не достигающего оператора return
, является плохой идеей. Это имеет смысл только в древнем устаревшем коде C, который использует int
как stand-in для void
, а в main
(хотя даже для main
мне лично нравится иметь явный return 0;
).
Ответ 3
Стандарт рассматривает его undefined.
На практике будет считана память или регистр, зарезервированный для возвращаемого значения. Что бы там ни было.
Ответ 4
C 2011 проект N1570
6.9.1 Определения функций
...
12 Если достигнут }
, который завершает функцию, а значение вызова функции используется
вызывающий, поведение не определено.
"Undefined" просто означает, что компилятор не требуется стандартом языка для обработки этой ситуации каким-либо особым образом; любое действие считается "правильным". Компилятор может свободно выполнять диагностику и останавливать перевод, или выдавать диагностический и полный перевод (это то, что вы видите), или просто игнорировать проблему полностью.
Что касается реального поведения во время выполнения, это зависит от таких вещей, как:
- как вызывающий абонент использует возвращаемое значение?
- что используется вызывающее соглашение?
- Как работает базовая архитектура?
и т.д..
Ответ 5
Компилятор не обязан диагностировать это, потому что в некоторых случаях он жесткий. Таким образом, правило состоит в том, что поведение undefined.
Ответ 6
Я провел простой тест на Linux 64 бит, GCC 4.63
Посмотрите на практике, как GCC собирает такую вещь.
Я создал простой пример
Это test.c с нормальным возвратным значением
int main()
{
return 0;
}
Это выход ассемблера GCC для test.c:
main:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl $0, %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
Это test2.c без возвращаемого значения
int main()
{
}
Это выход ассемблера GCC для test2.c:
main:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
В основном мы видим, что следующая строка отсутствует.
movl $0, %eax
Эта строка перемещает значение в регистр eax
, который является возвращаемым значением функции. Если функция использовалась в реальной жизни, регистр eax
, который, вероятно, содержит значение мусора, будет представлять возвращаемое значение main()
...
Ответ 7
Я провел тест в g ++. Кажется, что вы получаете объект со случайными вещами в нем, т.е. Точка не вызывает никакого конструктора. Когда я имею дело только с целыми числами, это означает, что я получаю случайное число. Когда я разбираюсь со строками, я получаю нарушение сегментации. Я не знаю, что вы имели в виду под коррупцией, но это плохо.
Что касается вашего первого вопроса, я бы пошел еще дальше. Я хочу знать, почему это предупреждение, которое отключено по умолчанию! Мне это кажется очень важным.
#include <string>
#include <iostream>
class X
{
private:
int _x;
public:
X(int x) : _x(x) { } // No default construcor!
int get() const { return _x; }
};
X test1(int x)
{
if (x > 0)
return X(x);
// warning: control reaches end of non-void function
}
class Y
{
private:
std::string _y;
public:
Y(std::string y) : _y(y) { } // No default construcor!
std::string get() const { return _y; }
};
Y test2(std::string y)
{
if (y.length() > 3)
return Y(y);
// warning: control reaches end of non-void function
}
int main(int, char**)
{
std::cout<<"4 -> "<<test1(4).get()<<std::endl;
std::cout<<"-4 -> "<<test1(-4).get()<<std::endl;
std::cout<<"stop -> "<<test2("stop").get()<<std::endl;
std::cout<<"go -> "<<test2("go").get()<<std::endl;
}