Що має бути головним () поверненням у C та C ++?


695

28
Я все ще думаю, що це теж досить неясно. Визначте для мене "найефективніший". Ефективна в якому сенсі? У сенсі зайняти менше пам’яті? У сенсі бігати швидше? Я можу побачити корисні відповіді, але я все ще думаю, що питання сформульовано досить погано.
Оноріо Катенач

7
Принаймні, контекст ефективності тут очевидний, особливо з прикладами (які, ймовірно, можуть уточнити визначення поняття "ефективний"). Сподіваємось, бідний буфер не повзав у яму і повністю шкодував про це питання. Можна сказати, незважаючи на недійсність чи int, значення повертається, тому воно не впливає на розмір файлу, виконані операції та не виділено пам'ять. І люди в більшості ОС схильні повертати 0 на успіх, а щось інше - на інший - успіх чи невдачу - але немає стандарту. Зрештою, жодної різниці в ефективності не було очевидним чином.
Kit10

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

2
Мені здається цікавим, що жодна з відповідей, наскільки я можу сказати, не дає повноцінного прикладу, включаючи #includeвисловлювання
puk

3
Повернені значення не мають сенсу на платформі без ОС. Ти ні до чого не повертаєшся. Якщо ви потрапили returnв main(...)на вбудованому пристрої, система переходить в непередбачуваному стані і ваша пральна машина буде самосвідомістю і спробувати вбити вас. Отже, ми використовуємо void main()в такому випадку. Це стандартна галузева практика вбудованого голого металу.
3Dave

Відповіді:


570

Повернене значення для mainпоказує, як програма вийшла. Нормальний вихід представлений значенням 0 повернення від main. Ненормальний вихід сигналізується ненульовим поверненням, але не існує стандарту інтерпретації ненульових кодів. Як зазначають інші, void main()це заборонено стандартом C ++ і його не слід використовувати. Дійсні mainпідписи C ++ :

int main()

і

int main(int argc, char* argv[])

що еквівалентно

int main(int argc, char** argv)

Варто також зазначити, що в C ++ int main()можна залишити без оператора return, після чого він за замовчуванням повертається 0. Це також стосується програми C99. Чи return 0;слід пропустити чи ні, відкрито для обговорення. Діапазон дійсних основних підписів програми C набагато більший.

Ефективність не є проблемою mainфункції. Його можна вводити та залишати лише один раз (позначаючи початок і закінчення програми) відповідно до стандарту C ++. Для C повторне введення main()дозволено, але його слід уникати.


69
основну МОЖНО вводити / залишати кілька разів, але ця програма, ймовірно, не виграє жодної нагороди за дизайн;)
korona

13
C99 також має помилку C ++, що досягнення кінця функції main () еквівалентно поверненню 0 - якщо main () визначено для повернення типу, сумісного з int (розділ 5.1.2.2.3).
Джонатан Леффлер

62
повторне введення основної недійсне C ++. Зовнішньо в стандарті 3.6.1.3 зазначено, що "основні не повинні використовуватися в рамках програми"
workmad3

117
stdlib.h забезпечує EXIT_SUCCESS та EXIT_FAILURE для цієї мети
Clay

20
0 і не нуль є правильними, але абсолютно безглуздими для того, хто читає ваш код. Це питання є доказом того, що люди не знають, що таке дійсні / недійсні коди. EXIT_SUCCESS / EXIT_FAILURE набагато чіткіше.
JaredPar

169

Схоже, прийнята відповідь орієнтована на C ++, тому я подумав, що я би додав відповідь, що стосується C, і це відрізняється кількома способами.

ISO / IEC 9899: 1989 (C90):

main() слід оголосити як:

int main(void)
int main(int argc, char **argv)

Або еквівалент. Наприклад, int main(int argc, char *argv[])еквівалентний другому. Крім того, intтип повернення можна опустити, оскільки він є за замовчуванням.

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

Стандарт визначає 3 значення для повернення, які суворо відповідають (тобто не покладаються на визначену реалізацією поведінку): 0і EXIT_SUCCESSдля успішного припинення, і EXIT_FAILUREдля невдалого припинення. Будь-які інші значення нестандартні та визначені реалізацією. main()повинні мати явнуreturn твердження в кінці, щоб уникнути невизначеної поведінки.

Нарешті, зі стандартної точки зору немає нічого поганого в дзвінках main() з програми.

ISO / IEC 9899: 1999 (C99):

Для C99 все те саме, що вище, за винятком:

  • The int повернення не може бути опущений.
  • Ви можете опустити заяву про повернення main(). Якщо ви зробите і main()закінчили, є неявне return 0.

1
@Lundin Я не думаю, що вам не потрібна цитата, щоб сказати, що комусь дозволяється робити компілятор, який приймає програми, що не відповідають стандартам, або мати компілятор, який не відповідає зіркам. Це загальне знання та здоровий глузд
KABoissonneault

4
@KABoissonneault Поведінка, визначена реалізацією, - це термін від стандарту, на відміну від повністю недокументованої поведінки. Якщо ви реалізуєте щось, що вказано як поведінка, визначена реалізацією, ви все одно дотримуєтесь стандарту. У цьому випадку C89, який цитується, не перераховує такої поведінки, визначеної реалізацією, а отже, і необхідність цитати, щоб довести, що він не просто вигадує речі не в силі.
Лундін

1
@Lundin Ви бачите це неправильно. Те, про що ми говоримо, - це не поведінка, визначена реалізацією, ми говоримо про те, що реалізація відхиляється від стандартної, якщо вони цього бажають. Це більше схоже на те, що дитина не слухається своїх батьків: вам не потрібна цитата від батьків, яка б вам сказала, яким чином дитина може піти проти того, що сказали батьки. Ви просто знаєте, що в момент, коли дитина вирішить це зробити, вони вже не відповідають правилам своїх батьків
KABoissonneault

2
@KABoissonneault Частина, яку я цитував у своєму коментарі, це, безумовно, про поведінку, що визначається реалізацією (на відміну від нестандартних розширень компілятора .) Таким чином, я кажу про поведінку, визначену реалізацією. Якщо у вас є монолог про щось інше, вам пощастить.
Лундін

1
@Lundin Я думаю, формулювання в цитаті є заплутаним (частина, де вони кажуть "але це визначає реалізацію програми"), але я впевнений, що людина говорила про нестандартну поведінку (як сказано в "Якщо реалізація дозволяє це "і" і більше не суворо відповідає [стандарту] ") на відміну від фактичної поведінки, визначеної реалізацією. Людина обов'язково повинна переформулювати свою відповідь, але я все ще не думаю, що цитата зі стандарту не потрібна на це
KABoissonneault

117

Стандарт C - Навколишнє середовище

Для розміщеного середовища (це нормальне) стандарт C11 (ISO / IEC 9899: 2011) говорить:

5.1.2.2.1 Запуск програми

Названа функція, викликана при запуску програми main. Реалізація не оголошує прототипу для цієї функції. Він повинен визначатися з типом повернення intта без параметрів:

int main(void) { /* ... */ }

або з двома параметрами (тут згадується як argcі argv, хоча будь-які імена можуть використовуватися, оскільки вони локальні для функції, в якій вони оголошені):

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

або еквівалент; 10) або іншим чином, визначеним реалізацією.

Якщо вони оголошені, параметри основної функції повинні дотримуватися таких обмежень:

  • Значення argcмає бути негативним.
  • argv[argc] має бути нульовим покажчиком.
  • Якщо значення argcбільше нуля, члени масиву argv[0]через argv[argc-1]включно повинні містити покажчики на рядки, яким задається реалізація значеннями хост-середовищем перед запуском програми. Наміром є подання програмної інформації, визначеної до запуску програми, з інших місць в розміщеному середовищі. Якщо хост-середовище не здатне подавати рядки з літерами як у великому, так і в малому регістрі, реалізація повинна забезпечити отримання рядків рядками.
  • Якщо значення argcбільше нуля, рядок, на який вказують, argv[0] представляє назву програми; argv[0][0]має бути нульовим символом, якщо назва програми недоступна в середовищі хоста. Якщо значення argcбільше одного, рядки, на які вказує argv[1]через, argv[argc-1] представляють параметри програми.
  • Параметри argcта argvта рядки, на які вказує argvмасив, повинні змінюватися програмою та зберігати їх останні збережені значення між запуском програми та завершенням програми.

10) Таким чином, intможе бути замінено іменем typedef, визначеним як int, або тип argvможе бути записаний як char **argvі так далі.

Завершення програми в C99 або C11

Значення, повернене з main(), передається у "середовище" визначеним реалізацією способом.

5.1.2.2.3 Завершення програми

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

11) Відповідно до 6.2.4, термін експлуатації об'єктів з автоматичною тривалістю зберігання, оголошений у main , закінчиться в першому випадку, навіть там, де їх не було б в другому.

Зауважте, що 0це "успіх". Ви можете використовувати EXIT_FAILUREі EXIT_SUCCESSз, <stdlib.h>якщо хочете, але 0 добре встановлено, і так це 1. Дивіться також Вихідні коди більше 255 - можливо? .

У C89 (а значить, і в Microsoft C) немає твердження про те, що відбувається, якщо main()функція повертається, але не вказує значення повернення; тому це призводить до невизначеної поведінки.

7.22.4.4 exitФункція

Finally5 Нарешті, контроль повертається у головне середовище. Якщо значення statusдорівнює нулю, або повертається EXIT_SUCCESSвизначена реалізацією форма успішного завершення стану . Якщо значення statusє EXIT_FAILURE, повертається визначена реалізацією форма невдалого припинення статусу . Інакше повернутий статус визначається реалізацією.

Стандартний C ++ - розміщене середовище

Стандарт C ++ 11 (ISO / IEC 14882: 2011) говорить:

3.6.1 Основна функція [basic.start.main]

¶1 Програма повинна містити глобальну функцію, яку називають основною, яка позначається початком програми. [...]

¶2 Реалізація не повинна визначати головну функцію. Ця функція не повинна перевантажуватися. Він повинен мати тип повернення типу int, але в іншому випадку визначено його тип реалізації. Усі реалізації повинні дозволяти обидва наступні визначення основних:

int main() { /* ... */ }

і

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

В останньому вигляді argcзазначається кількість аргументів, переданих програмі з середовища, в якому виконується програма. Якщо argcнемає нульових значень, ці аргументи подаються argv[0] через argv[argc-1]покажчики на початкові символи багатобайтових рядків, що закінчуються нулем (NTMBS) (17.5.2.1.4.2), і argv[0]повинні бути вказівниками на початковий символ NTMBS, який представляє ім'я, яке використовується для викликати програму або "". Значення argcмає бути негативним. Значення argv[argc] має бути 0. [Примітка. Рекомендується після (після) додавати будь-які інші (необов'язкові) параметри argv. —Закінчити примітку]

¶3 Функція mainне повинна використовуватися в межах програми. Зв'язок (3.5) з програми mainвизначається реалізацією. [...]

¶5 Оператор повернення в основному має наслідком залишення основної функції (знищення будь-яких об'єктів з автоматичною тривалістю зберігання) та виклик std::exitіз значенням повернення як аргумент. Якщо контроль доходить до кінця основного, не стикаючись з оператором return, ефект буде виконати

return 0;

Стандарт C ++ прямо говорить: "Він [основна функція] повинен мати тип повернення типу int, але в іншому випадку його тип визначений реалізацією", і вимагає тих же двох підписів, що і стандарт C, щоб підтримуватися як параметри. Таким чином, стандарт C ++ прямо не заборонено "void main ()", хоча він нічого не може зробити, щоб зупинити нестандартну реалізацію, що дозволяє альтернативи. Зауважте, що C ++ забороняє користувачеві телефонувати main(але стандарт C це не робить).

Там в пункті §18.5 Початок і закінчення в C ++ 11 стандарт , який ідентичний пункту з §7.22.4.4 функції в стандарті C11 (процитованому вище), за винятком виноски (який просто документи, і визначаються в ).exitEXIT_SUCCESSEXIT_FAILURE<cstdlib>

Стандарт С - загальне розширення

Класично системи Unix підтримують третій варіант:

int main(int argc, char **argv, char **envp) { ... }

Третій аргумент - це недійсний список покажчиків на рядки, кожен з яких є змінною середовища, яка має ім'я, знак рівності та значення (можливо, порожнє). Якщо ви цим не користуєтеся, ви все одно можете потрапити в оточення через " extern char **environ;". Ця глобальна змінна є унікальною серед POSIX тим, що вона не має заголовка, який оголошує її.

Це визнано стандартом C як загальне розширення, задокументоване у Додатку J:

J.5.1 Аргументи навколишнього середовища

¶1 У розміщеному середовищі головна функція отримує третій аргумент, char *envp[]який вказує на нульовий масив покажчиків на char, кожен з яких вказує на рядок, що забезпечує інформацію про середовище для цього виконання програми (5.1. 2.2.1).

Microsoft C

Microsoft VS 2010 компілятор цікаво. Веб-сайт говорить:

Синтаксис оголошення для основного є

 int main();

або, необов'язково,

int main(int argc, char *argv[], char *envp[]);

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

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

Цікаво, що MS не призначає двоаргументуючої версії, якої main()вимагають стандарти C та C ++. Він призначає лише три форми аргументів, де є третій аргумент char **envp, вказівник на список змінних середовища.

Сторінка Microsoft також перелічує деякі інші альтернативи - для wmain()яких потрібні широкі рядки символів та деякі інші.

Microsoft Visual Studio 2005 версія цієї сторінки НЕ список в void main()якості альтернативи. У версії від Microsoft Visual Studio 2008 роки зробити.

Стандарт C - Вільнодоступне середовище

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

5.1.2 Середовища виконання

Визначено два середовища виконання: автономне та розміщене. В обох випадках запуск програми відбувається, коли середовищем виконання викликається призначена функція C. Усі об'єкти зі статичною тривалістю зберігання повинні бути ініціалізовані (встановлені на їх початкові значення) перед запуском програми. Спосіб та терміни такої ініціалізації інакше не визначені. Закінчення програми повертає контроль до середовища виконання.

5.1.2.1 Вільне середовище

У незалежному середовищі (в якому виконання програми C може відбуватися без будь-якої переваги операційної системи) ім'я та тип функції, викликаної при запуску програми, визначаються реалізацією. Будь-які бібліотечні засоби, доступні для автономної програми, крім мінімального набору, передбаченого пунктом 4, визначаються реалізацією.

Вплив припинення програми у вільно розташованому середовищі визначено реалізацією.

Посилання на пункт 4 Відповідності стосується цього:

¶5 Програма, яка суворо відповідає, повинна використовувати лише ті особливості мови та бібліотеки, які визначені цим Міжнародним стандартом. 3) Він не повинен виводити продукцію, залежну від будь-якої визначеної, невизначеної чи визначеної реалізацією поведінки, і не повинен перевищувати будь-якого мінімального ліміту реалізації.

¶6 Дві форми відповідної реалізації є розміщеними та незалежними . У відповідності організовано впровадження повинно приймати будь-яку строго відповідну програму. У відповідності автономної реалізація повинна приймати будь-яку строго відповідну програму , в якій використання особливостей , зазначених в пункті бібліотеки (п 7) зводяться до змісту стандартних заголовків <float.h>, <iso646.h>, <limits.h>, <stdalign.h>, <stdarg.h>, <stdbool.h>, <stddef.h>, <stdint.h>, і <stdnoreturn.h>. Відповідна реалізація може мати розширення (включаючи додаткові функції бібліотеки), за умови, що вони не змінюють поведінку будь-якої суворо відповідної програми. 4)

¶7 Відповідна програма - це та, яка прийнятна для відповідної реалізації. 5)

3) Строго відповідна програма може використовувати умовні функції (див. 6.10.8.3), за умови, що використання захищається відповідною директивою попереднього оброблення умовного включення за допомогою відповідного макросу. Наприклад:

#ifdef __STDC_IEC_559__ /* FE_UPWARD defined */
    /* ... */
    fesetround(FE_UPWARD);
    /* ... */
#endif

4) Це означає, що відповідна реалізація не залишає ніяких ідентифікаторів, окрім явно зарезервованих у цьому Міжнародному стандарті.

5) Строго відповідні програми мають бути максимально портативними серед відповідних реалізацій. Програми узгодження можуть залежати від не портативних функцій відповідної реалізації.

Помітно, що єдиний заголовок, необхідний для окремо розташованого середовища, який фактично визначає будь-які функції, є <stdarg.h>(і навіть ті, які можуть бути - а часто - просто макроси).

Стандарт C ++ - Вільнодоступне середовище

Подібно до того, як стандарт C визнає як розміщене, так і вільно розташоване середовище, так і стандарт C ++. (Цитати ISO / IEC 14882: 2011.)

1.4 Відповідність реалізації [intro.compliance]

Are7 Визначено два види реалізації: розміщена реалізація та незалежна реалізація . Для розміщеної реалізації цей Міжнародний стандарт визначає набір доступних бібліотек. Автономна реалізація - це те, в якому виконання може відбуватися без переваги операційної системи та має визначений реалізацією набір бібліотек, який включає певні бібліотеки, що підтримують мову (17.6.1.3).

¶8 Відповідна реалізація може мати розширення (включаючи додаткові функції бібліотеки) за умови, що вони не змінюють поведінку будь-якої добре сформованої програми. Потрібні впровадження для діагностики програм, які використовують такі розширення, які неправильно формуються відповідно до цього Міжнародного стандарту. Однак, зробивши це, вони можуть компілювати та виконувати такі програми.

¶9 Кожна реалізація повинна містити документацію, яка ідентифікує всі умовно підтримувані конструкції, які вона не підтримує, та визначає всі особливості, характерні для локальної мови. 3

3) Ця документація також визначає поведінку, визначену реалізацією; див. 1.9.

17.6.1.3 Самостійні реалізації [відповідність]

Визначено два типи реалізацій: розміщені та автономні (1.4). Для розміщеної реалізації цей Міжнародний стандарт описує набір доступних заголовків.

Автономна реалізація має визначений реалізацією набір заголовків. Цей набір включає щонайменше заголовки, показані в таблиці 16.

Додається версія заголовка <cstdlib>оголошує , щонайменше функції abort, atexit, at_quick_exit, exit, і quick_exit(18,5). Інші заголовки, перелічені в цій таблиці, повинні відповідати тим же вимогам, що і для розміщеної реалізації.

Таблиця 16 - Заголовки C ++ для самостійних реалізацій

Subclause                           Header(s)
                                    <ciso646>
18.2  Types                         <cstddef>
18.3  Implementation properties     <cfloat> <limits> <climits>
18.4  Integer types                 <cstdint>
18.5  Start and termination         <cstdlib>
18.6  Dynamic memory management     <new>
18.7  Type identification           <typeinfo>
18.8  Exception handling            <exception>
18.9  Initializer lists             <initializer_list>
18.10 Other runtime support         <cstdalign> <cstdarg> <cstdbool>
20.9  Type traits                   <type_traits>
29    Atomics                       <atomic>

Що з використанням int main()C?

Стандарт §5.1.2.2.1 стандарту C11 показує кращі позначення -  int main(void)- але в стандарті також є два приклади, які показують int main(): § 6.5.3.4 ¶8 та § 6..7.6.3 ¶20 . Тепер важливо зазначити, що приклади не є "нормативними"; вони лише показові. Якщо в прикладах є помилки, вони не впливають безпосередньо на основний текст стандарту. Зважаючи на це, вони сильно вказують на очікувану поведінку, тому, якщо стандарт включає int main()в приклад, це дозволяє припустити, що int main()це не заборонено, навіть якщо це не є кращим позначенням.

6.5.3.4 Оператори sizeofта _Alignofоператори

EX8 ПРИКЛАД 3 У цьому прикладі обчислюється розмір масиву змінної довжини і повертається з функції:

#include <stddef.h>

size_t fsize3(int n)
{
    char b[n+3]; // variable length array
    return sizeof b; // execution time sizeof
}
int main()
{
    size_t size;
    size = fsize3(10); // fsize3 returns 13
    return 0;
}

@DavidBowling: Визначення функції, як int main(){ … }і вказує, що функція не бере аргументів, але не надає прототип функції, AFAICT. Бо main()це рідко є проблемою; це означає, що якщо у вас є рекурсивні дзвінки main(), аргументи не перевірятимуться. Для інших функцій це більше проблема - вам дійсно потрібен прототип в області застосування, коли функція викликається, щоб гарантувати правильність аргументів.
Джонатан Леффлер

1
@DavidBowling: Зазвичай ви не телефонуєте main()рекурсивно, за межами таких місць, як IOCCC. У мене є тестова програма, яка робить це - головним чином для новинок. Якщо у вас є int i = 0; int main() { if (i++ < 10) main(i, i * i); return 0; }і компілюється з GCC і не включається -Wstrict-prototypes, він збирається чисто під суворими попередженнями. Якщо це так main(void), він не може компілювати.
Джонатан Леффлер

61

Я вважаю, що main()слід повернути EXIT_SUCCESSабо EXIT_FAILURE. Вони визначені вstdlib.h


20
0 також є стандартним.
Кріс Янг

2
@ChrisYoung Існує EXIT_SUCCESSі EXIT_FAILUREтому, що деякі історичні операційні системи (VMS?) Використовували інше число, ніж 0, щоб позначити успіх. В даний час це 0 скрізь.
fuz

5
@FUZxxl ви праві, але це не суперечить моєму коментарю. EXIT_SUCCESS дійсно може бути ненульовим, але всі стандарти (C89, C99, C11) визначають 0 (як і EXIT_SUCCESS) як форму, що визначається реалізацією успішного припинення статусу.
Кріс Янг

2
@FUZxxl: Це правда, що VMS використовував непарні значення (як 1), щоб вказати на успіх і парні значення (як 0), щоб вказати на збій. На жаль, початковий стандарт ANSI C був інтерпретований так, що EXIT_SUCCESS повинен бути 0, тому повернення EXIT_SUCCESS з основного отримало саме неправильну поведінку на VMS. Портативне, що потрібно зробити для VMS, полягало в тому, щоб використовувати exit(EXIT_SUCCESS), що завжди робило правильно.
Адріан Маккарті

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

38

Зауважте, що стандарти C та C ++ визначають два види реалізації: автономне та розміщене.

  • C90 розміщене середовище

    Дозволені форми 1 :

    int main (void)
    int main (int argc, char *argv[])
    
    main (void)
    main (int argc, char *argv[])
    /*... etc, similar forms with implicit int */

    Коментарі:

    Перші два явно вказані як дозволені форми, інші явно дозволені, оскільки C90 дозволено "неявний int" для параметрів повернення типу та функції. Жодна інша форма не дозволена.

  • C90 окремо розташоване середовище

    Будь-яка форма або назва основного дозволена 2 .

  • C99 розміщене середовище

    Дозволені форми 3 :

    int main (void)
    int main (int argc, char *argv[])
    /* or in some other implementation-defined manner. */

    Коментарі:

    C99 видалено "неявний int" так main() він більше не дійсний.

    Введено дивне, неоднозначне речення "або в інший спосіб, визначений реалізацією". Це можна інтерпретувати як "параметри дляint main() можуть змінюватися", або як "основні можуть мати будь-яку форму, визначену реалізацією".

    Деякі компілятори вирішили трактувати стандарт останнім чином. Можливо, не можна легко стверджувати, що вони суворо не відповідають, цитуючи сам стандарт, оскільки це неоднозначно.

    Однак дозволити цілком дикі форми, main()мабуть, (?) Не було наміром цього нового речення. Обґрунтування С99 (не нормативне) передбачає, що речення стосується додаткових параметрів до int main 4 .

    І все ж розділ для припинення розміщеної програми оточуючого середовища продовжує сперечатися про випадок, коли main не повертається до int 5 . Хоча цей розділ не є нормативним щодо того, як слід оголосити головне, він, безумовно, означає, що основний може бути оголошений повністю визначеним реалізацією способом навіть у розміщених системах.

  • Вільне середовище C99

    Будь-яка форма або назва основного дозволена 6 .

  • C11 розміщене середовище

    Дозволені форми 7 :

    int main (void)
    int main (int argc, char *argv[])
    /* or in some other implementation-defined manner. */
  • C11 окремо стоїть середовище

    Будь-яка форма або назва основного дозволена 8 .


Зауважте, що int main()жодна з перелічених версій ніколи не була вказана як дійсна форма для будь-якої розміщеної реалізації C. У C, на відміну від C ++, ()і (void)мають різні значення. Перший - це застаріла особливість, яку можна видалити з мови. Дивіться майбутні мовні вказівки C11:

6.11.6 Декларатори функцій

Використання деклараторів функцій з порожніми дужками (не декларатори параметрів типу прототипу) є застарілою ознакою.


  • C ++ 03 розміщене середовище

    Дозволені форми 9 :

    int main ()
    int main (int argc, char *argv[])

    Коментарі:

    Зверніть увагу на порожні дужки у першій формі. C ++ і C в цьому випадку різні, оскільки в C ++ це означає, що функція не приймає жодних параметрів. Але в C це означає, що він може приймати будь-який параметр.

  • C ++ 03 вільно розташоване середовище

    Назва функції, викликаної при запуску, визначається реалізацією. Якщо вона названа, main()вона повинна відповідати зазначеним формам 10 :

    // implementation-defined name, or 
    int main ()
    int main (int argc, char *argv[])
  • C ++ 11 розміщене середовище

    Дозволені форми 11 :

    int main ()
    int main (int argc, char *argv[])

    Коментарі:

    Текст стандарту змінено, але він має те саме значення.

  • C ++ 11 вільно розташоване середовище

    Назва функції, викликаної при запуску, визначається реалізацією. Якщо вона названа, main()вона повинна відповідати зазначеним формам 12 :

    // implementation-defined name, or 
    int main ()
    int main (int argc, char *argv[])

Список літератури

  1. ANSI X3.159-1989 2.1.2.2 Розташоване середовище. "Запуск програми"

    Функція, що викликається при запуску програми, називається основною. Реалізація не оголошує прототипу для цієї функції. Він повинен визначатися з типом повернення int та без параметрів:

    int main(void) { /* ... */ } 

    або з двома параметрами (тут іменуються argc та argv, хоча будь-які імена можуть використовуватися, оскільки вони локальні для функції, в якій вони оголошені):

    int main(int argc, char *argv[]) { /* ... */ }
  2. ANSI X3.159-1989 2.1.2.1 Вільне середовище:

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

  3. ISO 9899: 1999 5.1.2.2 Розташоване середовище -> 5.1.2.2.1 Запуск програми

    Функція, що викликається при запуску програми, називається основною. Реалізація не оголошує прототипу для цієї функції. Він повинен визначатися з типом повернення int та без параметрів:

    int main(void) { /* ... */ } 

    або з двома параметрами (тут іменуються argc та argv, хоча будь-які імена можуть використовуватися, оскільки вони локальні для функції, в якій вони оголошені):

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

    або еквівалент; 9) або іншим способом, визначеним реалізацією.

  4. Обґрунтування міжнародного стандарту - Мови програмування - C, редакція 5.10. 5.1.2.2 Розташоване середовище -> 5.1.2.2.1 Запуск програми

    Поведінка аргументів до main та взаємодії вихідного, main та atexit (див. § 7.20.4.2) кодифіковано для обмеження небажаного різноманіття у поданні рядків argv та у значенні значень, повернених main.

    Специфікація argc та argv як аргументів для основного визнає широку попередню практику. Аргумент [argc] повинен бути нульовим покажчиком, щоб забезпечити надлишкову перевірку кінця списку, також на основі загальної практики.

    main - єдина функція, яку портативно можна оголосити або з нулем, або з двома аргументами. (Кількість аргументів інших функцій має точно збігатися між викликом та визначенням.) Цей особливий випадок просто визнає поширену практику відмови від аргументів до основних, коли програма не має доступу до рядків аргументів програми. Хоча багато реалізацій підтримують більш ніж два аргументи для основного, така практика не є ні благословенною, ні забороненою Стандартом; програма, яка визначає головне з трьома аргументами, не суворо відповідає (див. §J.5.1.).

  5. ISO 9899: 1999 5.1.2.2 Розташоване середовище -> 5.1.2.2.3 Завершення програми

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

  6. ISO 9899: 1999 5.1.2.1 Вільне середовище

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

  7. ISO 9899: 2011 5.1.2.2 Розташоване середовище -> 5.1.2.2.1 Запуск програми

    Цей розділ ідентичний цитованому вище C99.

  8. ISO 9899: 1999 5.1.2.1 Вільне середовище

    Цей розділ ідентичний цитованому вище C99.

  9. ISO 14882: 2003 3.6.1 Основна функція

    Реалізація не повинна визначати головну функцію. Ця функція не повинна перевантажуватися. Він повинен мати тип повернення типу int, але в іншому випадку його тип визначається реалізацією. Усі реалізації повинні дозволяти обидва наступні визначення основних:

    int main() { /* ... */ }

    і

    int main(int argc, char* argv[]) { /* ... */ }
  10. ISO 14882: 2003 3.6.1 Основна функція

    Це визначається реалізацією, чи потрібна програма у вільно розташованому середовищі для визначення основної функції.

  11. ISO 14882: 2011 3.6.1 Основна функція

    Реалізація не повинна визначати головну функцію. Ця функція не повинна перевантажуватися. Він повинен мати тип повернення типу int, але в іншому випадку його тип визначається реалізацією. Усі реалізації повинні дозволяти і те, і інше

    - функція () повернення int і

    - функція (int, вказівник на покажчик на char) повернення int

    як тип основного (8.3.5).

  12. ISO 14882: 2011 3.6.1 Основна функція

    Цей розділ ідентичний цитованому вище C ++ 03.


Одне запитання: чи означають стандарти C ++, що визначено також підпис функції запуску у вільно існуючих середовищах? Наприклад, реалізація могла б визначити функцію запуску такою: int my_startup_function ()або int my_startup_function (int argc, char *argv[])може вона мати, наприклад: char my_startup_function (long argc, int *argv[])як функцію запуску? Гадаю, ні, правда? Крім того, це теж неоднозначно?
Утку

@Utku Він може мати будь-який підпис, якщо він не названий, main()тому що він повинен використовувати один з перелічених підписів. Я б міг уявити, що найпоширенішим з них буде void my_startup_function (), оскільки не має сенсу повертатися з програми на автономних системах.
Лундін

1
Розумію. Але якщо для функції запуску дозволено використовувати будь-яке ім’я та будь-який підпис, чому б не дозволити також використовувати інший підпис main? Вибачте, якщо це не розумне питання, але я не міг зрозуміти міркування.
Утку

@Utku C і C ++ там різні. Щодо того, чому C ++ виконує це, я не маю уявлення, немає обґрунтування. Я підозрюю, що головним винуватцем (призначений каламбур) є Строуструп, який рано заявив, що головний повинен повернути Int, period. Тому що, коли він зробив першу версію C ++, його використовували лише для розміщення систем. У пов'язаному дописі Stroustrup все ще здається забутим про існування самостійно реалізованих реалізацій: наприклад, він необізнано посилається на розміщену підрозділ реалізації стандарту C, ігноруючи існування глави 5.1.2.1.
Лундін

1
Примітна річ у стандартній чернетці C11 - це те, що, хоча func()він вважається застарілим, сам проект використовує int main()у власних прикладах.
Антті Хаапала

29

Поверніть 0 на успіх і не-нуль за помилку. Це стандарт, що використовується сценаріями UNIX та DOS, щоб дізнатися, що сталося з вашою програмою.


8

main() у C89 та K&R C не визначені типи повернення за замовчуванням до 'int'.

return 1? return 0?
  1. Якщо ви не введете заяву про повернення в int main(), закриття {поверне 0 за замовчуванням.

  2. return 0або return 1буде отримано батьківським процесом. У оболонці він переходить у змінну оболонки, і якщо ви запускаєте свою програму, формуйте оболонку і не використовуєте цю змінну, то вам не потрібно турбуватися про повернене значення main().

Див. Як я можу отримати те, що повернулась моя основна функція? .

$ ./a.out
$ echo $?

Таким чином ви можете бачити, що саме ця змінна $?отримує найменше значущий байт зворотного значення main().

У сценаріях Unix і DOS return 0зазвичай повертаються успіх і нуль для помилок. Це стандарт, що використовується сценаріями Unix і DOS для з'ясування того, що сталося з вашою програмою та контролю над усім потоком.


4
Строго кажучи, $?це не змінна середовище; це попередньо визначена оболонка (або вбудована) змінна. Різницю важко помітити, але якщо запустити env(без жодних аргументів), вона надрукує середовище, і $?не відображатиметься в оточенні.
Джонатан Леффлер

1
Повернення 0 автоматично, коли основні «падіння кінця» є лише в C ++ і C99 далі, а не в C90.
Каз

Друкарська помилка: "закриття {" має бути }. Так що я не дозволю мені змінити цю маленьку.
Спенсер

7

Майте на увазі, що, хоча ви повертаєте інт, деякі ОС (Windows) усіляють повернене значення в один байт (0-255).


4
Unix робить те саме, що ймовірно більшість інших операційних систем. Я знаю, що VMS робить такі неймовірні дивні речі, що повернення нічого, крім EXIT_SUCCESS або EXIT_FAILURE, вимагає неприємностей.
Леон Тіммерманс

2
MSDN просить відрізнятись: коли повідомляється через mscorlib, вихідний код - це підписане 32-бітове ціле число . Це, мабуть, означає, що бібліотеки виконання C, які скорочують коди виходу, несправні.

Так, це неправильно. У Windows 32-бітове ціле число повертається (і перетворюється в unsigned). Це те саме в системах UNIX з 32-бітовими цілими числами. Але оболонки у стилі UNIX в будь-якій системі зазвичай зберігають лише неподписане 8-бітове ціле число.
Джон Макфарлейн

4

Повернене значення може використовуватись операційною системою для перевірки того, як програма була закрита.

Повернене значення 0 зазвичай означає ОК у більшості операційних систем (тих, про які я все-таки думаю).

Це також можна перевірити, коли ви викликаєте процес самостійно, і побачите, чи програма завершилася та завершена належним чином.

Це НЕ просто умова програмування.


У питанні немає нічого, що вказувало б на наявність оперативної системи. Повернення значення не має жодного сенсу в автономній системі.
Лундін

3

Повернене значення main()показує, як програма вийшла. Якщо значення повернення - zeroце означає, що виконання було успішним, тоді як будь-яке ненульове значення буде означати, що у виконанні щось пішло погано.


1
Це коментар, а не відповідь на запитання.
Лундін

2

У мене склалося враження, що стандарт вказує, що main не потребує значення повернення, оскільки успішне повернення базувалося на ОС (нуль в одному може бути або успіхом, або відмовою в іншому), тому відсутність повернення є підказкою для компілятор, щоб вставити успішне повернення.

Однак я зазвичай повертаю 0.


C99 (і C ++ 98) дозволяють опустити операцію повернення з основного; C89 не дозволяє вам опустити заяву про повернення.
Джонатан Леффлер

Це коментар, а не відповідь.
Лундін

Це не дає відповіді на запитання. Щоб критикувати або вимагати роз'яснення у автора, залиште коментар під їх дописом.
Стів Лілліс

6
@SteveLillis: У 2008 році SO не мала розділу з коментарями.
graham.reeds

2

Повертаючи 0, слід сказати програмісту, що програма успішно закінчила завдання.


При поверненні 1 із main()звичайних сигналів сталася помилка; повернення 0 сигналів про успіх. Якщо ваші програми завжди виходять з ладу, тоді 1 добре, але це не найкраща ідея.
Джонатан Леффлер

1
@JonathanLeffler: Значення повернення 1з mainвизначено реалізацією. Єдина мова , певні значення 0, EXIT_SUCCESS(часто визначається як 0), і EXIT_FAILURE. У OpenVMS return 1;позначає успішне припинення.
Кіт Томпсон

VMS не є "нормальним" - в тому сенсі, що я сказав. Хіба це не щось на кшталт «будь-яке дивне значення є успіхом; навіть значення є відмовою 'у VMS?
Джонатан Леффлер

2

Опустіть return 0

Коли програма C або C ++ досягає кінця, mainкомпілятор автоматично генерує код, щоб повернути 0, тому немає необхідності ставити return 0;явно в кінці main.

Зауважте: коли я роблю цю пропозицію, майже завжди незмінно супроводжується одним із двох видів коментарів: "Я цього не знав". або "Це погана порада!" Моє обґрунтування полягає в тому, що безпечно і корисно покладатися на поведінку компілятора, яка явно підтримується стандартом. Для C, починаючи з C99; див. розділ 5.1.2.2.3 ISO / IEC 9899: 1999:

[...] повернення від початкового виклику до mainфункції еквівалентно виклику exitфункції зі значенням, поверненим mainфункцією як її аргументом; досягнувши того, }що завершує mainфункцію, повертає значення 0.

Для C ++, починаючи з першого стандарту в 1998 році; див. розділ 3.6.1 ISO / IEC 14882: 1998:

Якщо контроль доходить до кінця основного, не стикаючись з оператором return, ефект полягає у виконанні return 0;

Всі версії обох стандартів з тих пір (C99 і C ++ 98) підтримували одну і ту ж ідею. Ми покладаємось на автоматично сформовані функції учасників у C ++, і мало хто пише явні return;заяви в кінці voidфункції. Причини проти пропущення, схоже, зводиться до "це виглядає дивно" . Якщо, як я, вам цікаво обґрунтування зміни стандарту С, прочитайте це запитання . Також зауважте, що на початку 1990-х це вважалося "неохайною практикою", оскільки в той час це було не визначене поведінка (хоча широко підтримується).

Крім того, основні вказівки C ++ містять декілька випадків пропуску return 0;в кінці mainта жодних випадків, коли написано явне повернення. Хоча в цьому документі ще немає конкретного настанови щодо цієї конкретної теми, це здається принаймні мовчазним схваленням цієї практики.

Тож я виступаю за те, щоб її пропустити; інші не погоджуються (часто з жорстокістю!) У будь-якому випадку, якщо ви зіткнетеся з кодом, який його омиває, ви дізнаєтесь, що він прямо підтримується стандартом, і ви будете знати, що це означає.


2
Це погана порада, оскільки компілятори, які реалізують лише C89, а не будь-який пізній стандарт, все ще є надзвичайно поширеними (про це пишу в 2017 році) і залишатимуться надзвичайно поширеними в осяжному майбутньому. Наприклад, останній раз я перевірив, чи не було введено версію компіляторів Microsoft, що реалізували C99, і я розумію, що це все ще характерно для компіляторів вбудованої системи, які не є GCC.
zwol

4
@zwol: У кожного, у кого немає іншого вибору, крім використання компілятора, застарілого на 28 років, напевно, є більше проблем, ніж вирішення питання про чітке включення return 0;, проте зауважу, що багато компіляторів тієї епохи також імпліцитно реалізували return 0;ще до того, як це було стандартизовані.
Едвард

2
Насправді я дуже багато вбудованих систем працюю і не стикався з компілятором, який не підтримує неявні return 0десятиліття. Також поточні версії Microsoft C підтримують його , а також . Можливо, ваша інформація застаріла?
Едвард

2
Я можу оцінити, що це майже суперечливо в C, (за @zwol). У C ++ будь-яка суперечка навколо цього є чистою дурницею.
Гонки легкості по орбіті

2
@Edward Я не сказав, що суперечки не існували, я сказав, що це нісенітниця: P
Гонки легкості в орбіті

1

Що повернути, залежить від того, що ви хочете зробити з виконуваним файлом. Наприклад, якщо ви використовуєте свою програму з оболонкою командного рядка, вам потрібно повернути 0 для успіху, а нуль - для відмови. Тоді ви зможете використовувати програму в оболонках з умовною обробкою залежно від результату вашого коду. Також ви можете призначити будь-яке ненулеве значення відповідно до вашої інтерпретації, наприклад, для критичних помилок різні точки виходу програми можуть припинити програму з різними значеннями виходу, і яке доступне оболонці виклику, яка може вирішити, що робити, перевіряючи повернене значення. Якщо код не призначений для використання з оболонками і повернене значення нікого не турбує, його можна опустити. Я особисто використовую підписint main (void) { .. return 0; .. }


Формат main () визначається реалізацією, тобто компілятором значень. Програміст не може вибрати, яку форму вибрати, за винятком випадків, коли компілятор підтримує кілька форм.
Лундінь

@Lundin Тип повернення буде реалізовано впровадженням. Але значення, яке потрібно повернути, визначає програміст. C99 Розділ 5.1.2.2.3 зазначає, що тип повернення mainсумісний із int. Тому повернення intне буде проблемою. Хоча інші типи повернення дозволені, але в цьому випадку змінна середовища, що має значення повернення, не буде визначена. Але якщо програміст робить це return 0;в bash, його можна використовувати для створення гілок.
фоксис

1

Якщо у вас дійсно виникають проблеми, пов’язані з ефективністю повернення цілого числа з процесу, вам, ймовірно, слід уникати виклику цього процесу стільки разів, що це повернене значення стає проблемою.

Якщо ви це робите (викликайте процес стільки разів), ви повинні знайти спосіб ввести свою логіку безпосередньо всередині абонента або у файл DLL, не виділяючи конкретного процесу для кожного виклику; багаторазові розподіли процесу приносять вам відповідну проблему ефективності в цьому випадку.

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


1

Ось невелика демонстрація використання зворотних кодів ...

При використанні різних інструментів, які надає термінал Linux, можна використовувати код повернення, наприклад, для обробки помилок після завершення процесу. Уявіть, що в моєму файлі є текстовий файл:

Це певний приклад, щоб перевірити, як працює греп.

При виконанні команди grep створюється процес. Після того, як він пройшов (і не зламався), він повертає деякий код між 0 і 255. Наприклад:

$ grep order myfile

Якщо ти зробиш

$ echo $?
$ 0

ви отримаєте 0. Чому? Оскільки grep знайшов збіг та повернув вихідний код 0, що є звичайним значенням для успішного виходу. Давайте перевіримо це ще раз, але з тим, що не знаходиться в нашому текстовому файлі, і тому не буде знайдено відповідності:

$ grep foo myfile
$ echo $?
$ 1

Оскільки grep не зміг узгодити маркер "foo" із вмістом нашого файлу, код повернення дорівнює 1 (це звичайний випадок, коли виникає помилка, але, як зазначено вище, ви можете вибрати багато значень).

Тепер наступний сценарій bash (просто введіть його в терміналі Linux), хоча дуже базовий повинен дати деяке уявлення про помилки:

$ grep foo myfile
$ CHECK=$?
$ [ $CHECK -eq 0] && echo 'Match found'
$ [ $CHECK -ne 0] && echo 'No match was found'
$ No match was found

Після того, як другий рядок нічого не друкується в термінал, оскільки "foo" зробив grep return 1, і ми перевіряємо, чи повертається код повернення grep, рівний 0. Другий умовний вираз повторює його повідомлення в останньому рядку, оскільки це вірно через CHECK == 1.

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


У сценарії оболонки ви використовуєте if grep foo myfile; then echo 'Match found'; else echo 'No match was found'; fi- тестування повернення безпосередньо. Якщо ви хочете зафіксувати статус (для звітування тощо), ви використовуєте завдання. Ви можете використовувати if grep foo myfile; CHECK=$?; [ "$CHECK" = 0 ]; then echo 'Match found'; else echo 'No match was found'; fiабо використовувати три рядки. Ви також можете використовувати варіанти -sі -qдля grepзапобігання сірники або звичайних повідомлень про помилки з'являються. Однак це деталі оболонки - ключовий момент, що стан виходу може бути корисним - це нормально.
Джонатан Леффлер

1

Який правильний (найефективніший) спосіб визначити головну () функцію в C і C ++ - int main () або void main () - і чому?

Ці слова "(найефективніше)" не змінюють питання. Якщо ви не знаходитесь у незалежному середовищі, існує один загально правильний спосіб заявити main(), і це як повернення int.

Що має main()повернутися в C і C ++?

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

Якщо int main (), тоді повернути 1 або повернути 0?

0 за успіх, ненульовий за невдачу. Знову ж таки, не те, що вам потрібно (або дістати) до вибору: це визначається інтерфейсом, якому ви повинні відповідати.

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