На этот вопрос уже есть ответ здесь:
В чем разница между оператором return и exit в программировании на C при вызове из любой точки C-программы?
В C нет большой разницы при использовании в функции запуска программы (которая может быть main ()
, wmain ()
, _tmain ()
или имя по умолчанию, используемое вашим компилятором).
Если вы возвращаете
в main ()
, управление возвращается к функции _start ()
в библиотеке C, которая изначально запустила вашу программу, которая затем в любом случае вызывает exit ()
. Так что на самом деле не имеет значения, какой из них вы используете.
Единственный случай, когда оба делают (почти) одно и то же - это функция main()
, поскольку возврат из main выполняет exit()
.
Пример с return
:
#include <stdio.h>
void f(){
printf("Executing f\n");
return;
}
int main(){
f();
printf("Back from f\n");
}
Если вы выполните эту программу, она выведет:
Executing f Возврат от f
Другой пример для exit()
:
#include <stdio.h>
#include <stdlib.h>
void f(){
printf("Executing f\n");
exit(0);
}
int main(){
f();
printf("Back from f\n");
}
Если вы выполните эту программу, она выведет:
Выполнение f
Вы никогда не получите "Back from f". Также обратите внимание на #include
, необходимый для вызова библиотечной функции exit()
.
Также обратите внимание, что параметр exit()
является целым числом (это статус возврата процесса, который может получить процесс запуска; обычно используется 0 для успеха или любое другое значение для ошибки).
Параметром оператора return является любой возвращаемый тип функции. Если функция возвращает void, то return в конце функции можно опустить.
И последнее, exit()
бывает двух видов _exit()
и exit()
. Разница между ними заключается в том, что exit()
(и возврат из main) вызывает функции, зарегистрированные с помощью atexit()
или on_exit()
, прежде чем действительно завершить процесс, тогда как _exit()
(из #include
, или его синоним _Exit из #include
) завершает процесс немедленно.
Теперь есть проблемы, специфичные для C++.
C++ выполняет гораздо больше работы, чем C, при выходе из функций (return
-ing). В частности, он вызывает деструкторы локальных объектов, выходящих из области видимости. В большинстве случаев программистов мало волнует состояние программы после остановки процесса, поэтому это не будет иметь большого значения: выделенная память будет освобождена, файловые ресурсы закрыты и так далее. Но это может иметь значение, если ваш деструктор выполняет IOs. Например, автоматический C++ OStream
, созданный локально, не будет промыт при вызове exit, и вы можете потерять некоторые не промытые данные (с другой стороны, статический OStream
будет промыт).
Этого не произойдет, если вы используете старые добрые потоки C FILE*
. Они будут промыты при exit()
. На самом деле, правило такое же, как и для зарегистрированных функций завершения, FILE*
будет промываться при всех нормальных завершениях, что включает exit()
, но не вызовы _exit()
или abort().
Вы также должны помнить, что C++ предоставляет третий способ выхода из функции: выброс исключения. Этот способ выхода из функции вызовет деструктор. Если оно не будет поймано где-либо в цепочке вызывающих, исключение может дойти до функции main() и завершить процесс.
Деструкторы статических объектов C++ (globals) будут вызваны, если вы вызовете return
из main()
или exit()
в любом месте вашей программы. Они не будут вызваны, если программа завершается с помощью _exit()
или abort()
. abort()
в основном полезен в режиме отладки с целью немедленного завершения программы и получения трассировки стека (для посмертного анализа). Обычно он скрыт за макросом assert()
, активным только в режиме отладки.
Когда полезен exit() ?
exit()
означает, что вы хотите немедленно остановить текущий процесс. Это может быть полезно для работы с ошибками, когда мы сталкиваемся с какой-то неустранимой проблемой, которая не позволяет вашему коду больше делать что-либо полезное. Часто это удобно, когда поток управления сложен и коды ошибок приходится распространять по всему пути. Но имейте в виду, что это плохая практика кодирования. Молчаливое завершение процесса в большинстве случаев является худшим поведением, и следует предпочесть фактическое управление ошибками (или в C++ использование исключений).
Прямые вызовы exit()
особенно плохи, если они делаются в библиотеках, так как это обрекает пользователя библиотеки на гибель, и пользователь библиотеки должен сам решать, реализовывать или нет какое-то восстановление ошибок. Если вам нужен пример того, почему вызов exit()
из библиотеки - это плохо, то это приводит к тому, что люди задают этот вопрос.
Существует неоспоримое законное использование exit()
как способ завершения дочернего процесса, запущенного с помощью fork() в операционных системах, поддерживающих эту функцию. Возврат к коду, предшествующему fork(), обычно является плохой идеей. Именно это объясняет, почему функции семейства exec() никогда не возвращаются к вызывающей стороне.