Есть ли разница между return n и exit (n) в C?

9

Есть ли разница между return nmainфункции) и exit(n)в C? Это определяется стандартами C или POSIX или зависит от ОС или компилятора?

Томас Оуэнс
источник

Ответы:

5

В большинстве случаев нет никакой разницы, но вот программа на C, которая может вести себя по-разному в зависимости от того, использует она return 0;или exit(0);:

#include <stdio.h>
#include <stdlib.h>

static char *message;

void cleanup(void) {
    printf("message = \"%s\"\n", message);
}

int main(void) {
    char local_message[] = "hello, world";
    message = local_message;
    atexit(cleanup);
#ifdef USE_EXIT
    puts("exit(0);");
    exit(0);
#else
    puts("return 0;");
    return 0;
#endif
}

Из-за atexit()вызова, либо exit(0);или return 0;Заставляет cleanupфункцию , которая будет вызвана. Разница в том, что если программа вызывает exit(0);, очистка происходит, когда «вызов» main()все еще активен, поэтому local_messageобъект все еще существует. Выполнение return 0;, однако, немедленно завершает вызов main()и затем вызывает cleanup()функцию. Так как cleanup()ссылается (через глобальный messageуказатель) на объект, который выделен локально main, и этот объект больше не существует, поведение не определено.

Вот поведение, которое я вижу в моей системе:

$ gcc -DUSE_EXIT c.c -o c && ./c
exit(0);
message = "hello, world"
$ gcc c.c -o c && ./c
return 0;
message = ""
$ 

Запуск программы без каких- -DUSE_EXITлибо действий, включая сбой или печать "hello, world"(если используемая память local_messageне засорена).

Однако на практике это различие проявляется только в том случае, если объекты, определенные локально внутри, main()становятся видимыми снаружи main(), сохраняя указатели на них. Это может произойти правдоподобно argv. (Эксперимент на моей системе показывает, что объекты, на которые указывают, argvпостепенно *argvпродолжают существовать после возвращения из main(), но вы не должны зависеть от этого.)

Кит Томпсон
источник
16
  • Для C
    Стандарт говорит, что возврат от начального вызова к main эквивалентен вызову exit. Однако нельзя ожидать, что возврат из main будет работать, если во время очистки могут потребоваться данные, локальные для main.

  • Для C ++

Когда exit (0) используется для выхода из программы, деструкторы для нестатических объектов локальной области не вызываются. Но деструкторы вызываются, если используется return 0.

Программа 1 - - использует exit (0) для выхода

#include<iostream>
#include<stdio.h>
#include<stdlib.h>

using namespace std;

class Test {
public:
  Test() {
    printf("Inside Test's Constructor\n");
  }

  ~Test(){
    printf("Inside Test's Destructor");
    getchar();
  }
};

int main() {
  Test t1;

  // using exit(0) to exit from main
  exit(0);
}

Вывод: внутри конструктора теста

Программа 2 - использует возврат 0 для выхода

#include<iostream>
#include<stdio.h>
#include<stdlib.h>

using namespace std;

class Test {
public:
  Test() {
    printf("Inside Test's Constructor\n");
  }

  ~Test(){
    printf("Inside Test's Destructor");
  }
};

int main() {
  Test t1;

   // using return 0 to exit from main
  return 0;
}

Вывод: внутри конструктора
теста внутри деструктора теста

Вызов деструкторов иногда важен, например, если деструктор имеет код для освобождения ресурсов, таких как закрытие файлов.

Обратите внимание, что статические объекты будут очищены, даже если мы вызовем exit (). Например, см. Следующую программу.

#include<iostream>
#include<stdio.h>
#include<stdlib.h>

using namespace std;

class Test {
public:
  Test() {
    printf("Inside Test's Constructor\n");
  }

  ~Test(){
    printf("Inside Test's Destructor");
    getchar();
  }
};

int main() {
  static Test t1;  // Note that t1 is static

  exit(0);
}

Вывод: внутри конструктора
теста внутри деструктора теста

Вайбхав Агарвал
источник
Закрытие файлов на самом деле не является хорошим примером важного деструктора, срабатывающего при выходе, потому что файлы будут закрыты при выходе из программы в любом случае.
Уинстон Эверт
1
@WinstonEwert: Верно, но могут существовать буферы уровня приложения, которые еще нужно очистить.
Филипп
1
Вопрос нигде не упоминает C ++ ...
tdammers
Я не знаю ни одного языка, так что прости меня, но этот ответ заставляет меня думать, что выход подобен отказоустойчивости C #, поэтому в C ++ выход в попытке выполнить выполняется наконец?
Джимми Хоффа
@JimmyHoffa, c ++ не имеетfinally
Уинстон Эверт
6

Стоит отметить, что стандарт C (C99) определяет два типа сред выполнения: автономная среда и размещенная среда . Автономная среда - это среда C, которая не поддерживает библиотеки C и предназначена для встроенных приложений и тому подобного. Среда AC, которая поддерживает библиотеки C, называется размещенной средой.

C99 говорит, что в автономной среде завершение программы определяется реализацией. Таким образом, если реализация определяет main, return nи exitих поведение такое, как определено в этой реализации.

C99 определяет поведение размещенной среды как,

Если возвращаемый тип основной функции является совместимым с ней типом, возврат из начального вызова основной функции эквивалентен вызову функции выхода со значением, возвращаемым основной функцией в качестве аргумента; достижение}, завершающего основную функцию, возвращает значение 0. Если возвращаемый тип несовместим с int, состояние завершения, возвращаемое в хост-среду, не указывается.

Тед
источник
1
И действительно нет смысла вызывать exit () из автономной среды. Встроенный эквивалент exit () будет заключаться в том, чтобы повесить программу в вечном цикле, а затем подождать, пока сторожевой таймер истечет.
0

С точки зрения стандарта Си, не совсем, кроме returnтого, чтобы быть оператором и exit()быть функцией. Любая из них приведет к вызову любых функций, зарегистрированных с, atexit()с последующим завершением программы.

Есть несколько ситуаций, которые вы должны остерегаться:

  • Рекурсия в main(). Хотя это редко встречается на практике, это допустимо в C. (C ++ явно запрещает это.)
  • Повторное использование main(). Иногда существующее main()будет переименовано во что-то другое и будет вызвано новым main().

Использование exit()создаст ошибку, если что-то из этого произойдет после того, как вы написали код, особенно если не завершится ненормально. Чтобы избежать этого, полезно иметь привычку трактовать ее main()как функцию и использовать, returnкогда вы хотите, чтобы она закончилась.

Blrfl
источник