В чем разница между операцией return и exit в программировании на C при вызове из любой точки программы C?
В чем разница между выходом и возвратом?
Ответ 1
- return - это инструкция языка, возвращающегося из вызова функции.
- 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 Back from f
Другой пример для exit()
:
#include <stdio.h>
#include <stdlib.h>
void f(){
printf("Executing f\n");
exit(0);
}
int main(){
f();
printf("Back from f\n");
}
Если вы выполните эту программу, она напечатает:
Executing f
Вы никогда не получите "Назад с f". Также обратите внимание на #include <stdlib.h>
, необходимую для вызова библиотечной функции exit()
.
Также обратите внимание, что параметр exit()
является целым числом (это статус возврата процесса, который может получить процесс запуска, обычное использование - 0 для успеха или любое другое значение для ошибки).
Параметр оператора return - это любой тип возвращаемого значения функции. Если функция возвращает void, вы можете опустить возврат в конце функции.
Последняя точка, exit()
представлена в двух вариантах _exit()
и exit()
. Разница между формами заключается в том, что exit()
(и возврат из основного) вызывает функции, зарегистрированные с использованием atexit()
или on_exit()
, прежде чем действительно завершить процесс, а _exit()
(из #include <unistd.h>
или его синонимом _Exit от #include <stdlib.h>
) немедленно прекращает процесс.
Теперь есть также проблемы, характерные для С++.
С++ выполняет гораздо большую работу, чем C, когда он выходит из функций (return
-ing). В частности, он вызывает деструкторы локальных объектов, выходящих из области видимости. В большинстве случаев программисты не будут заботиться о состоянии программы после остановки процесса, поэтому это не имеет большого значения: выделенная память будет освобождена, файл закрыт и т.д. Но это может иметь значение, если ваш деструктор выполняет IO. Например, автоматическое создание С++ OStream
локально не будет очищено при вызове для выхода, и вы можете потерять некоторые незафиксированные данные (с другой стороны, статический OStream
будет сброшен).
Это не произойдет, если вы используете старые добрые потоки C FILE*
. Они будут сброшены на exit()
. На самом деле правило такое же, что и для зарегистрированных функций выхода, FILE*
будет сбрасываться на всех нормальных окончаниях, включая exit()
, но не вызывает _exit()
или abort().
Вы также должны иметь в виду, что С++ предоставляет третий способ выйти из функции: выброс исключения. Этот способ выхода из функции вызовет деструктор. Если он не попадает в цепочку вызывающих абонентов, исключение может перейти к функции main() и завершить процесс.
Деструкторы статических объектов С++ (globals) будут вызываться, если вы вызываете либо return
из main()
, либо exit()
в любом месте вашей программы. Они не будут вызваны, если программа завершается с помощью _exit()
или abort()
. abort()
в основном полезен в режиме отладки с целью немедленной остановки программы и получения трассировки стека (для анализа post mortem). Обычно он скрывается за макросом assert()
, только активным в режиме отладки.
Когда функция exit() полезна?
exit()
означает, что вы хотите немедленно остановить текущий процесс. Это может быть полезно для управления ошибками, когда мы сталкиваемся с какой-то безвозвратной проблемой, которая больше не позволит вашему коду делать что-нибудь полезное. Это часто бывает удобно, когда поток управления сложный, и коды ошибок должны распространяться повсюду. Но имейте в виду, что это неправильная практика кодирования. Тише завершение процесса в большинстве случаев должно быть предпочтительнее худшее поведение и фактическое управление ошибками (или на С++ с использованием исключений).
Прямые вызовы exit()
особенно плохи, если они выполняются в библиотеках, поскольку они будут обрекать пользователя библиотеки, и это должен быть выбор пользователя библиотеки, чтобы реализовать какое-то исправление ошибок или нет. Если вам нужен пример того, почему вызов exit()
из библиотеки плох, это приводит, например, к людям, которые задают этот вопрос.
Существует неоспоримое законное использование exit()
в качестве способа завершения дочернего процесса, запущенного fork() в операционных системах, поддерживающих его. Возвращаясь к коду перед fork(), как правило, это плохая идея. Это объяснение того, почему функции семейства exec() никогда не возвращаются к вызывающему.
Ответ 2
Я написал две программы:
int main(){return 0;}
и
#include <stdlib.h>
int main(){exit(0)}
После выполнения gcc -S -O1
. Вот что я нашел, наблюдая
при сборке (только важные части):
main:
movl $0, %eax /* setting return value */
ret /* return from main */
и
main:
subq $8, %rsp /* reserving some space */
movl $0, %edi /* setting return value */
call exit /* calling exit function */
/* magic and machine specific wizardry after this call */
Итак, мой вывод: используйте return
, когда можете, и exit()
, когда вам нужно.
Ответ 3
В C нет большой разницы при использовании в стартовой функции программы (которая может быть main()
, wmain()
, _tmain()
или имя по умолчанию, используемое вашим компилятором).
Если вы return
в main()
, управление возвращается к функции _start()
в библиотеке C, которая изначально запускала вашу программу, а затем вызывает exit()
. Так что не важно, какой из них вы используете.
Ответ 4
оператор возврата выходит из текущей функции, а exit() выходит из программы
they are the same when used in main() function
также return - инструкция, а exit() - это функция, для которой требуется файл заголовка stdlb.h