return оператор vs exit () в main ()


197

Чи варто використовувати exit()або просто returnзаяви у main()? Особисто я віддаю перевагу returnтвердженням, бо відчуваю, що це як читання будь-якої іншої функції, а контроль потоку, коли я читаю код, є плавним (на мій погляд). І навіть якщо я хочу переробити цю main()функцію, мені returnздається кращим вибором, ніж exit().

Чи exit()робить щось особливе, що returnні?

Відповіді:


277

На насправді, це відмінність, але це тонка. Це має більше наслідків для C ++, але відмінності важливі.

Коли я дзвоню returnв main(), деструктори будуть викликатися для моїх локально контекстних об'єктів. Якщо я зателефоную exit(), жоден деструктор не буде викликаний для моїх локально обстежених об'єктів! Перечитайте це. exit() не повертається . Це означає, що як тільки я його називаю, "немає спини". Будь-які об’єкти, які ви створили в цій функції, не будуть знищені. Часто це не має ніяких наслідків, але іноді це відбувається, як закриття файлів (напевно, ви хочете, щоб усі ваші дані були передані на диск?).

Зауважте, що staticоб’єкти будуть очищені, навіть якщо ви телефонуєте exit(). Нарешті зауважте, що якщо ви використовуєтеabort() , жоден об’єкт не буде знищений. Тобто жодні глобальні об'єкти, статичні об'єкти та локальні об'єкти не будуть викликати своїх руйнівників.

Продовжуйте обережно, коли надаєте перевагу виїзду над поверненням.

http://groups.google.com/group/gnu.gcc.help/msg/8348c50030cfd15a


1
abort () виходить із умовою помилки (ненульовий код виходу) і може бути навіть ядром. Якщо вам потрібно вийти без виклику статичних деструкторів, використовуйте _exit.

7
@Mike: є різниця між буферами файлів бібліотеки C та об'єктами потоку файлів C ++. exit () - будучи частиною бібліотеки C - був розроблений для узгодження та змивання перших, але може обійти останнє: навіть стандартний вміст fstream C ++ не перекидається на диск (спробуйте - я це зробив, він не вдається з / Linux / GCC), і очевидно, що визначені користувачем типи, які буферизували введення-виведення, також не можна очікувати, що вони змиваються.
Тоні Делрой

9
Примітка: Заява: жоден деструктор не буде викликаний для моїх локально обстежених об'єктів! більше не відповідає дійсності для C ++ 11: - Об'єкти, пов'язані з поточним потоком із тривалістю зберігання потоку, знищуються (лише для C ++ 11). cplusplus.com/reference/cstdlib/exit
Ilendir

7
Це означає, що thread_localдеструктори об'єктів будуть викликані. Руйнівники для інших локальних об’єктів досі не викликаються. ideone.com/Y6Dh3f
HolyBlackCat

3
BTW, і просто бути педантичним і тому, що ця відповідь все ще може бентежити читачів, які використовують C: Для C питання щодо exit()чистого закриття файлів насправді неправильне. Єдині дані про час не можуть бути промиті в протилежному випадку: тобто , якщо використовувати returnз main()і один назвали setbuf()або setvbuf()з буфером оголошений як автоматичне зберігання в main()(як описано у відповіді R. Це нижче). Це дуже погано, що це питання позначене як C, так і C ++ (а стиль кодування - це не проблема стилю!).
Грег А. Вудс

25

Ще одна відмінність: exitце функція Стандартна бібліотека, тому вам потрібно включити заголовки та зв’язатись зі стандартною бібліотекою. Для ілюстрації (на C ++) це дійсна програма:

int main() { return 0; }

але для використання exitвам знадобиться:

#include <stdlib.h>
int main() { exit(EXIT_SUCCESS); }

Плюс це додає додаткове припущення: що виклик exitз mainмає ті ж побічні ефекти , як повернення до нуля. Як зазначали інші, це залежить від того, який саме виконаний файл ви будуєте (тобто, хто дзвонить main). Ви кодуєте додаток, що використовує C-час виконання? Плагін Майя? Служба Windows? Водій? Кожен випадок потребує досліджень, щоб визначити, чи exitє рівнозначним return. Використання IMHO, exitколи ви справді маєте на увазі, return просто робить код більш заплутаним. ОТОХ, якщо ви дійсно маєте на увазі exit , то будь-якими способами користуйтеся цим.


16

Існує хоча б одна причина, щоб віддати перевагу exit: Якщо хтось із ваших atexitобробників посилається на дані про тривалість автоматичного зберігання в main, або якщо ви використовували setvbufабо setbufпризначаєте одному із стандартних потоків буфер тривалості автоматичного зберігання main, то повертаючись із mainпродукту невизначена поведінка, але виклик exitдійсний.

Ще одне потенційне використання (як правило, зарезервоване для іграшкових програм, однак) - це вихід із програми з рекурсивними викликами main.


1
@Seb в цьому немає нічого особливого main()- це лише функція, як і будь-яка інша. З іншого боку, оскільки він має особливу згадку в стандарті, стандарт повинен бути досить уважним щодо того, як він визначає main()і речі, близькі і дорогі йому. Однак зрештою, хоча стандарт не вимагає (і не повинен ) компіляторів робити щось особливе щодо автоматичного зберігання в main(). Будь ласка, не забудьте прочитати виноску № 11 нижче абзацу, на який ви посилаєтесь у коментарі.
Грег А. Вудс

1
@ GregA.Woods Цікаво. Здається, що якийсь нормативний текст суперечить деякому інформативному тексту. Згідно з директивами ISO / IEC , нормативне посилання вважається "незамінним", де - оскільки інформативність вважається лише додатковою ... Крім того, використання слова "буде" для передачі вимоги недійсне; згідно з вищезгаданим документом (додаток Н). Підсумовуючи це, інформативний текст, безумовно, недійсний.
аутист

2
@Seb: Очевидно, що наміри не перевищують вимог щодо поведінки автоматичного зберігання, і виноска, очевидно, була написана для уточнення цього. Так, у стандарті C є неточна, погана формулювання. Кожен, хто її прочитав, знає це. Ми також знаємо, що комітет, як правило, не вирішує подібних питань, оскільки наміри вже очевидні.
R .. GitHub СТОП ДОПОМОГАТИ ICE

1
@Seb: Це не дебати чи судові справи, щоб довести, що ви праві. Метою повинно бути чітке розуміння того, що є власне мовою С (як задумано та як реалізовано), та висловлення цього у відповідях, корисних читачам. Нормативний текст є явно неправильним (всупереч наміру того, що він повинен був виразити) таким чином, що по суті фіксується виноском. Якщо ви цим не задоволені, надішліть звіт про дефекти, але не очікуйте відповіді. Ось як котиться WG14 ...
R .. GitHub

3
@Seb: Ви, здається, вірите, що мову С можна зрозуміти, інтерпретуючи текст на природній мові стандарту, немовби він суворий. Це просто неможливо. Специфікація містить помилки, і WG14 не витрачає свій час на перезапис матеріалів, коли проста зноска уточнює, що вони вже знають, що допустили помилку, але що читач може зрозуміти це.
R .. GitHub СТОП ДОПОМОГАТИ ICE

5

Я завжди використовую, returnоскільки стандартний прототип для main()говорить, що він повертає int.

Однак, деякі версії стандартів надають mainспеціальну обробку і припускають, що вона повертає 0, якщо немає явного returnтвердження. Дано наступний код:

int foo() {}
int main(int argc, char *argv[]) {}

G ++ генерує лише попередження про foo()ігнорування відсутнього повернення main:

% g++ -Wall -c foo.cc
foo.cc: In function int foo()’:
foo.cc:1: warning: control reaches end of non-void function

Я не знаю про C, але стандарт C ++ визначає, що якщо ви не повернете значення в основному, передбачається повернути 0.
Джейсон Бейкер

Схоже, що C99 те саме: faq.cprogramming.com/cgi-bin/…
Джейсон Бейкер

2
C99 і C ++ повертають 0, якщо немає заяви про повернення, C90 не робить.
d0k

Тільки тому, що функція оголошена як повертаюче значення, не означає, що ви повинні використовувати її returnдля завершення її виконання. Виклик exit()також є дійсним, а іноді і необхідним способом припинити виконання будь-якої функції. Насправді, як я та інші описали в інших місцях, виклик exit()навіть з main()переказує набагато більш чіткий намір вийти з процесу, зберігає автоматичне зберігання до виходу з процесу і полегшує технічне обслуговування під час майбутнього рефакторингу коду. Для С з використанням returnв , main()коли намір полягає в тому, щоб завершити процес, таким чином , можливо , погана практика.
Грег А. Вудс

@ GregA.Вважає, це ваша думка, але навряд чи заслуговує голосу вниз! Те, що я написав вище, повністю відповідає стандарту , тоді як ваш аргумент - це лише семантика.
Альнітак

5

Я НАЙДІЛЬКИЙ другий коментар Р. про використання exit (), щоб уникнути автоматичного зберігання у main()рекультивації до того, як програма фактично закінчиться. return X;Заява в main()неточно еквівалентно виклику exit(X);, так як динамічне зберігання main()зникає , коли main()повертається, але він не звертається в нуль , якщо виклик exit()зроблений замість цього.

Крім того, на C або будь-якій мові, подібній С, returnвисловлювання сильно натякає читачеві, що виконання буде продовжуватися у функції виклику, і хоча це продовження виконання зазвичай технічно вірно, якщо рахувати процедуру запуску C, яка викликала вашу main()функцію, це не саме те, що ви маєте на увазі, коли маєте намір закінчити процес.

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

Отже, поєднуючи всі ці моменти разом, робимо висновок, що це погана звичка , принаймні для C, використовувати returnзаяву для завершення програми в main().


Ви можете знайти 5.1.2.2.3p1 стандарту С цікаво ...
аутизм

Цю відповідь варто уважно розглянути для програм С, як це контекстно зазначено у відповіді. Для використання з C ++ його потрібно ретельно зважити проти всіх раніше згаданих застережень. Що стосується C ++, я б пропонував уникати exit()взагалі, але використовувати його, якщо throwабо abort()альтернативи не працюють у конкретному контексті. Але особливо уникайте exit()в основному, а використовувати віддачу в основному замість цього як типову практику.
Eljay

5

Чи робить () повернення?

Деякі компілятори для незвичайних платформ exit()можуть перевести його аргумент у значення виходу вашої програми, тоді як повернення з main()просто може передати значення безпосередньо в середовище хоста без будь-якого перекладу.

Стандарт вимагає однакової поведінки в цих випадках (конкретно, він говорить, що повернення чогось, що є intсумісним, main()повинно бути еквівалентним виклику exit()з цим значенням). Проблема полягає в тому, що різні ОС мають різні умови інтерпретації значень виходу. У багатьох системах (МНОГО!) 0 означає успіх, а все інше - невдачу. Але, скажімо, VMS, непарні значення означають успіх, а парні - невдачу. Якщо ви повернули 0 з main(), користувач VMS побачить неприємне повідомлення про порушення доступу. Насправді не було порушення доступу - це було просто стандартне повідомлення, пов’язане з кодом відмови 0.

Потім ANSI підійшов і благословив EXIT_SUCCESSі EXIT_FAILUREяк аргументи, які ви могли передати exit(). Стандарт також говорить , що exit(0)повинен вести себе так само exit(EXIT_SUCCESS), тому більшість реалізацій визначають EXIT_SUCCESSв 0.

Таким чином, стандарт прив'язує вас до VMS, оскільки він не залишає стандартного способу повернення коду відмови, який, мабуть, має значення 0.

Тому компілятор VAX / VMS C початку 1990-х років не інтерпретував повернене значення main(), а просто повертав будь-яке значення хост-середовищу. Але якщо ви використовуєте, exit()це зробить те, що вимагає стандарт: перекладіть EXIT_SUCCESS(або 0) в код успіху і EXIT_FAILUREв загальний код відмови. Щоб користуватися EXIT_SUCCESS, вам довелося передати його exit(), ви не змогли повернути його main(). Я не знаю, чи зберегли таку поведінку більш сучасні версії цього компілятора.

Портативна програма С, яка виглядала так:

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

int main() {
  printf("Hello, World!\n");
  exit(EXIT_SUCCESS);  /* to get good return value to OS */
  /*NOTREACHED*/ /* to silence lint warning */
  return 0;  /* to silence compiler warning */
}

Убік: Якщо я пам'ятаю правильно, умова VMS для значень виходу є більш нюансованою, ніж непарною / парною. Він фактично використовує щось на зразок низьких трьох біт для кодування рівня суворості. Взагалі кажучи, однакові рівні вираженості вказували на успіх чи іншу інформацію, а парні вказували на помилки.


Деякі старі компілятори до ANSI, можливо, трактували це значення returnedпо- mainрізному від значення, переданого до exit- але стандарт спеціально говорить: "Якщо тип повернення mainфункції є типом, сумісним int, повернення з початкового виклику доmain функції є еквівалент виклику exitфункції зі значенням, поверненим mainфункцією як її аргументом ". Це С11; C89 / C90 мали майже однакове формулювання.
Кіт Томпсон,

Справді. Тим не менш, деякі компілятори епохи ANSI не отримали цього права і вимагали явного використання виходу, щоб повернути правильне значення повернення в середовище хоста. Оскільки стандарт (навіть тоді) вимагає, щоб 0 ставився так само, якEXIT_SUCCESS , , не було можливості повернути специфічний для платформи статус відмови зі значенням 0, через що деякі компілятори епохи трактували return-from-main і по- exit()різному.
Адріан Маккарті

У вас є цитування на це? Окреме питання полягає в тому, чи є якісь поточні компілятори саме цієї помилки. Ваша відповідь - це словосполучення в теперішньому часі.
Кіт Томпсон

Це справедлива критика. Я змінив формулювання, щоб обмежити сферу застосування конкретного випадку, про який я знаю.
Адріан Маккарті

0

У C повернення з main- це точно те саме, що дзвонити exitз тим же значенням.

Розділ 5.1.2.2.3 стандартних стандартів С :

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

Правила для C ++ дещо відрізняються, як зазначено в інших відповідях.


-1

Там насправді є різниця між exit(0)і return(0)в main- коли вашаmain функція викликається кілька разів.

Наступна програма

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

int main(int argc, char** argv) {
  if (argc == 0)
    return(0);
  printf("%d", main(argc - 1, argv));
}

Виконати як

./program 0 0 0 0

Це призведе до наступного результату:

00000

Однак цей:

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

int main(int argc, char** argv) {
  if (argc == 0)
    exit(0);
  printf("%d", main(argc - 1, argv));
}

Не буде надруковано нічого незалежно від аргументів.

Якщо ви впевнені, що ніхто ніколи не подзвонить вам mainпрямо, технічно це не велика різниця, але підтримувати більш чіткий код exitбуде виглядати набагато краще. Якщо ви з якоїсь причини хочете зателефонувати main- вам слід налаштувати його під свої потреби.

Якщо говорити про C.

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