В чем разница между выходом и возвратом? [duplicate]

На этот вопрос уже есть ответ здесь:

В чем разница между оператором return и exit в программировании на C при вызове из любой точки C-программы?

86
задан Flexo 6 September 2016 в 17:12
поделиться

2 ответа

В C нет большой разницы при использовании в функции запуска программы (которая может быть main () , wmain () , _tmain () или имя по умолчанию, используемое вашим компилятором).

Если вы возвращаете в main () , управление возвращается к функции _start () в библиотеке C, которая изначально запустила вашу программу, которая затем в любом случае вызывает exit () . Так что на самом деле не имеет значения, какой из них вы используете.

9
ответ дан 24 November 2019 в 08:01
поделиться
  • 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
Возврат от 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() никогда не возвращаются к вызывающей стороне.

138
ответ дан 24 November 2019 в 08:01
поделиться
Другие вопросы по тегам:

Похожие вопросы: