Чи є різниця між поверненням n і виходом (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
    Standard каже, що повернення від початкового виклику до основного є рівнозначним виклику виходу. Однак не можна очікувати, що повернення з основного не спрацює, якщо під час очищення можуть знадобитися дані, локальні до основних.

  • Для C ++

Коли вихід (0) використовується для виходу з програми, деструктори для нестатичних об'єктів, які охоплюються локально, не викликаються. Але деструктори викликаються, якщо використовується return 0.

Програма 1 - - використовує вихід (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 - використовує return 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, яке не підтримує бібліотеки С і призначене для вбудованих додатків тощо. Середовище змінного струму, яке підтримує бібліотеки С, називається середовищем розміщення.

C99 зазначає, що в окремо встановленому середовищі закінчення програми визначено реалізацією. Отже, якщо реалізація визначає main, return nі exit, їх поведінка є такою, як визначено в цій реалізації.

C99 визначає поведінку навколишнього середовища як,

Якщо тип повернення основної функції - це сумісний з нею тип, повернення від початкового виклику до основної функції еквівалентно виклику функції виходу зі значенням, поверненим основною функцією як її аргументом; досягнення}, що завершує основну функцію, повертає значення 0. Якщо тип повернення не сумісний з int, статус припинення, повернутий у середовище хоста, не визначений.


1
І насправді не має сенсу викликати exit () з незалежної середовища. Вбудованим еквівалентом exit () було б повісити програму у вічному циклі, а потім дочекатися, коли сторожовий час вичерпається.

0

З точки зору стандарту С, насправді, крім returnтого, що це твердження та exit()функція. Будь-яке atexit()виклик будь-яких функцій, зареєстрованих з, викликається після закінчення програми.

Є кілька ситуацій, на які слід стежити:

  • Рекурсія в main(). Хоча це рідко зустрічається на практиці, він легальний у C. (C ++ прямо забороняє це.)
  • Повторне використання main(). Іноді існуючий main()буде перейменований на щось інше і буде називатися новим main().

Використання exit()введе помилку, якщо будь-яка з них трапиться після того, як ви написали код, особливо якщо не закінчується аномально. Щоб уникнути цього, корисно мати звичку трактувати main()як функцію, яку вона є, і використовувати, returnколи ви хочете, щоб вона закінчилася.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.