В чем разница между операцией 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