Что должно возвращать main () в C и C ++?


694

28
Я все еще думаю, что это довольно расплывчато тоже. Определите «самый эффективный» для меня. Эффективен в каком смысле? В смысле занимать меньше памяти? В смысле бегать быстрее? Я вижу полезные ответы, но я все еще думаю, что вопрос сформулирован довольно плохо.
Онорио Катеначчи

7
На самом деле, контекст эффективного здесь очевиден, особенно с примерами (которые, вероятно, там проясняют определение «эффективного»). Надеюсь, бедный буфер не попал в дыру и не пожалел об этом полностью. Можно сказать, что независимо от void или 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;Должен ли он быть опущен или нет, открыт для обсуждения. Диапазон действительных основных сигнатур программы на Си намного больше.

Эффективность не проблема с mainфункцией. Он может быть введен и оставлен только один раз (отмечен запуск и завершение программы) в соответствии со стандартом C ++. Для C повторный ввод main()разрешен, но его следует избегать.


69
main МОЖЕТ быть введен / оставлен несколько раз, но эта программа, вероятно, не получит никаких наград за дизайн;)
korona

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

62
повторный вход в основной не является допустимым C ++. В стандарте в явном виде 3.6.1.3 говорится, что «main не должен использоваться в программе»
workmad3

117
Для этой цели stdlib.h предоставляет EXIT_SUCCESS и EXIT_FAILURE
Глина

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

169

Принятый ответ, по-видимому, предназначен для C ++, поэтому я подумал, что добавлю ответ, относящийся к C, и он отличается в нескольких отношениях.

ИСО / МЭК 9899: 1989 (С90):

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 все то же самое, что и выше, за исключением:

  • Тип intвозврата не может быть опущен.
  • Вы можете опустить оператор возврата из main(). Если вы делаете, и main()закончил, есть неявное return 0.

1
@ Лундин Я не думаю, что вам нужна цитата, чтобы сказать, что кому-то разрешено создавать компилятор, который принимает программы, не соответствующие стандартам, или иметь компилятор, не соответствующий стандартам. Это общее знание и здравый смысл
KABoissonneault

4
@KABoissonneault Поведение, определяемое реализацией, является термином из стандарта, а не полностью недокументированным поведением. Если вы реализуете что-то, что указано как поведение, определяемое реализацией, вы все равно будете следовать стандарту. В этом случае C89, который был процитирован, не перечисляет такого поведения, определенного реализацией, отсюда и необходимость цитирования, чтобы доказать, что он не просто придумывает что-то неожиданное.
Лундин

1
@Lundin Вы видите это неправильно. Мы говорим не о поведении, определяемом реализацией, мы говорим о реализации, отклоняющейся от стандарта, если они того пожелают. Это больше похоже на то, как ребенок не подчиняется своим родителям: вам не нужна цитата из родителей, чтобы сказать вам, как ребенок может пойти против того, что сказали родители. Вы просто знаете, что в тот момент, когда ребенок решает это сделать, он больше не соответствует принципам
гильдии

2
@ KABoissonneault Часть, которую я процитировал в своем комментарии, определенно относится к поведению, определяемому реализацией (в отличие от нестандартных расширений компилятора ). Таким образом, я говорю о поведении, определяемом реализацией. Если у вас есть монолог о чем-то другом, удачи вам в этом.
Лундин

1
@Lundin Я думаю, что формулировка в цитате сбивает с толку (часть, где они говорят «но это определяет реализацию программы»), но я почти уверен, что человек говорил о нестандартном поведении (как сказано в «Если реализация разрешает это «и» и больше не строго соответствует [стандарту] »в отличие от фактического поведения, определяемого реализацией. Человек должен определенно перефразировать свой ответ, но я все еще не думаю, что цитата из стандарта необходима для этого
KABoissonneault

117

Стандарт С - размещенная среда

Для размещенной среды (это нормальная среда) стандарт 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] inclusive должны содержать указатели на строки, которым перед запуском программы передаются значения, определяемые реализацией средой хоста. Намерение состоит в том, чтобы предоставить программе информацию, определенную до ее запуска, из другого места в размещенной среде. Если среда хоста не способна снабжать строки буквами как в верхнем, так и в нижнем регистре, реализация должна обеспечивать получение строк в нижнем регистре.
  • Если значение 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]

Program1 Программа должна содержать глобальную функцию, называемую main, которая является назначенным началом программы. [...]

An2 Реализация не должна предопределять основную функцию. Эта функция не должна быть перегружена. Он должен иметь возвращаемый тип типа int, но в противном случае его тип определяется реализацией. Все реализации должны позволять оба следующих определения main:

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определяется реализацией. [...]

Statement5 Оператор return в main приводит к выходу из функции main (уничтожению любых объектов с автоматическим хранением) и вызову std::exitс возвращаемым значением в качестве аргумента. Если управление достигает конца main, не встречая оператора return, эффект заключается в выполнении

return 0;

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

Там в пункте §18.5 Начало и окончание в C ++ 11 стандарт , который идентичен пункту из §7.22.4.4 функции в стандарте C11 (процитированном выше), за исключением сноски (который просто документы, и определяются в )exitEXIT_SUCCESSEXIT_FAILURE<cstdlib>

Стандарт C - Общее расширение

Классически системы Unix поддерживают третий вариант:

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

Третий аргумент представляет собой список указателей на строки с нулевым символом в конце, каждый из которых представляет собой переменную окружения, которая имеет имя, знак равенства и значение (возможно, пустое). Если вы не используете это, вы все равно можете попасть в среду через ' extern char **environ;'. Эта глобальная переменная уникальна среди тех, что в POSIX, потому что у нее нет заголовка, который ее объявляет.

Это признано стандартом C как общее расширение, документированное в Приложении J:

J.5.1 Аргументы среды

In1 В размещенной среде главная функция получает третий аргумент, char *envp[]который указывает на массив указателей с нулевым символом в конце char, каждый из которых указывает на строку, которая предоставляет информацию о среде для этого выполнения программы (5.1. 2.2.1).

Microsoft C

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

Синтаксис объявления для main

 int main();

или, необязательно,

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

В качестве альтернативы, mainи wmainфункции могут быть объявлены не возвращается void(без возвращаемого значения). Если вы объявляете mainили wmainвозвращаете void, вы не можете вернуть код завершения родительскому процессу или операционной системе с помощью оператора return. Чтобы вернуть код завершения, когда mainили wmainобъявлено как void, вы должны использовать exitфункцию.

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

Интересно, что MS не предписывает версию с двумя аргументами, main()которая требуется для стандартов C и C ++. Он только предписывает форму с тремя аргументами, где третий аргумент - char **envpуказатель на список переменных среды.

На странице Microsoft также перечислены некоторые другие альтернативы, wmain()которые принимают строки широких символов, и некоторые другие.

Версия этой страницы для Microsoft Visual Studio 2005 не указана в качестве альтернативы. В версии от Microsoft Visual Studio 2008 года сделать.void main()

Стандарт С - автономная среда

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

5.1.2 Среды исполнения

Определяются две среды исполнения: автономная и размещенная. В обоих случаях запуск программы происходит, когда назначенная функция C вызывается средой выполнения. Все объекты со статической продолжительностью хранения должны быть инициализированы (установлены их начальные значения) перед запуском программы. Способ и время такой инициализации в остальном не определены. Завершение программы возвращает управление среде выполнения.

5.1.2.1 Отдельно стоящая среда

В автономной среде (в которой выполнение программы на С может происходить без какой-либо выгоды от операционной системы), имя и тип функции, вызываемой при запуске программы, определяются реализацией. Любые библиотечные средства, доступные для автономной программы, кроме минимального набора, требуемого в разделе 4, определяются реализацией.

Эффект завершения программы в автономной среде определяется реализацией.

Перекрестная ссылка на пункт 4 Соответствие относится к этому:

A5 В строго соответствующей программе должны использоваться только те функции языка и библиотеки, которые указаны в настоящем международном стандарте. 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 ++. (Цитаты из ИСО / МЭК 14882: 2011.)

1.4 Соответствие реализации [intro.compliance]

¶7 Определены два вида реализаций: размещенная реализация и автономная реализация . Для размещенной реализации этот международный стандарт определяет набор доступных библиотек. Автономная реализация - это та, в которой выполнение может осуществляться без использования операционной системы, и имеет определенный набор реализаций библиотек, который включает в себя определенные библиотеки поддержки языка (17.6.1.3).

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

Each9 Каждая реализация должна включать документацию, которая идентифицирует все условно поддерживаемые конструкции, которые она не поддерживает, и определяет все специфичные для локали характеристики. 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операторы

...

Пример 8 В этом примере размер массива переменной длины вычисляется и возвращается из функции:

#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 из main привело к совершенно неправильному поведению в VMS. Переносной вещью для VMS было использование exit(EXIT_SUCCESS), которое всегда делало правильные вещи.
Адриан Маккарти

1
5.1.2.2.3 «Если тип возвращаемого значения основной функции является типом, совместимым с int, возврат из начального вызова основной функции эквивалентен вызову функции выхода со значением, возвращаемым основной функцией в качестве аргумента; 11) при достижении}, завершающего функцию main, возвращается значение 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 удалил "implicit int", поэтому main()больше не действителен.

    Было введено странное, неоднозначное предложение «или каким-то другим образом, определяемым реализацией». Это может быть либо интерпретировано как «параметры int main()могут различаться», либо как «main может иметь любую форму, определяемую реализацией».

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

    Однако разрешить совершенно дикие формы, main()вероятно, (?) Не было целью этого нового предложения. Обоснование C99 (не нормативное) подразумевает, что предложение относится к дополнительным параметрам к int main 4 .

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

  • C99 автономная среда

    Разрешается любая форма или имя главного 6 .

  • С11 размещенная среда

    Разрешенные формы 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 Объявление функций

Использование деклараторов функций с пустыми скобками (не деклараторы типа параметров в формате prototype) является устаревшей функцией.


  • 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 Отдельно стоящая среда:

    В автономной среде (в которой выполнение программы на С может происходить без какой-либо выгоды от операционной системы), имя и тип функции, вызываемой при запуске программы, определяются реализацией.

  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 и взаимодействия exit, main и atexit (см. §7.20.4.2) было кодифицировано, чтобы ограничить некоторое нежелательное разнообразие в представлении строк argv и в значении значений, возвращаемых main.

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

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

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

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

  6. ISO 9899: 1999 5.1.2.1 Отдельно стоящая среда

    В автономной среде (в которой выполнение программы на С может происходить без какой-либо выгоды от операционной системы), имя и тип функции, вызываемой при запуске программы, определяются реализацией.

  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, но в остальном его тип определяется реализацией. Все реализации должны позволять оба следующих определения main:

    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, указатель на указатель на символ), возвращающая 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 ++ обеспечивает это, я понятия не имею, нет никакого обоснования. Я подозреваю, что главный виновник (предназначение каламбура) - Страуструп, который рано заявил, что main должен вернуть int, точка. Потому что, когда он сделал первую версию C ++, он использовался только для хост-систем. В связанном посте Страуструп по- прежнему не замечает существования автономных реализаций: например, он невежественно ссылается на главу о размещенной реализации стандарта C, игнорируя существование главы 5.1.2.1.
Лундин

1
Что касается стандартного черновика C11, то следует отметить, что, хотя func()он считается устаревшим, сам черновик использует int main()свои собственные примеры.
Антти Хаапала

29

Вернуть 0 в случае успеха и ненулевое значение в случае ошибки. Это стандарт, используемый сценариями UNIX и DOS для определения того, что случилось с вашей программой.


8

main() в C89 и K & R для неопределенных типов возвращаемых данных по умолчанию используется значение 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

Имейте в виду, что, даже если вы возвращаете int, некоторые ОС (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) позволяют вам опустить оператор return из main; 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; см. ИСО / МЭК 9899: 1999, раздел 5.1.2.2.3:

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

Для C ++, начиная с первого стандарта в 1998 году; см. ИСО / МЭК 14882: 1998 раздел 3.6.1:

Если управление достигает конца main, не встречая оператора return, результатом является выполнение return 0;

С тех пор все версии обоих стандартов (C99 и C ++ 98) придерживались той же идеи. Мы полагаемся на автоматически сгенерированные функции-члены в C ++, и немногие люди пишут явные return;операторы в конце voidфункции. Причины против пропуска, кажется, сводятся к «выглядит странно» . Если, как и я, вам интересно узнать причину изменения стандарта Си, прочитайте этот вопрос . Также обратите внимание, что в начале 1990-х это считалось «небрежной практикой», потому что в то время это было неопределенное поведение (хотя и широко поддерживаемое).

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

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


2
Это плохой совет, потому что компиляторы, которые реализуют только C89, а не какой-либо более поздний стандарт, все еще чрезвычайно распространены (я пишу это в 2017 году) и останутся чрезвычайно распространенными в обозримом будущем. Например, в последний раз я не проверял ни одну версию компиляторов Microsoft, реализующих C99, и, насколько я понимаю, это все еще характерно для компиляторов встроенных систем, которые не являются GCC.
Звол

4
@zwol: Любой, у кого нет выбора, кроме как использовать компилятор, который устарел к 28 годам, вероятно, имеет больше проблем, чем решение о том, включать ли его явно return 0;, однако я хотел бы отметить, что многие компиляторы той эпохи также реализовали неявный return 0;еще до того, как он был стандартизированы.
Эдвард

2
На самом деле, я много работаю со встроенными системами и не сталкивался с компилятором, который не поддерживает неявное return 0уже более десяти лет. Также текущие версии Microsoft C поддерживают это также . Возможно, ваша информация устарела?
Эдвард

2
Я могу оценить это, будучи спорным в C, почти (на @zwol). В C ++ любое противоречие вокруг этого - полная чушь.
Гонки

2
@ Эдвард Я не сказал, что противоречия не существует, я сказал, что это чепуха: P
Гонки

1

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


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

@Lundin Возвращаемый тип будет реализацией реализацией. Но значение, которое должно быть возвращено, определяется программистом. C99 Раздел 5.1.2.2.3 упоминает, что возвращаемый тип mainсовместим с int. Поэтому возвращение intне будет проблемой. Хотя допускаются и другие возвращаемые типы, но в этом случае переменная среды, имеющая возвращаемое значение, не будет указана. Но если это делает программист, return 0;то в bash его можно использовать для создания веток.
Phoxis

1

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

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

Подробно, если вы хотите узнать, является ли возврат 0 более или менее эффективным, чем возврат 1, в некоторых случаях это может зависеть от компилятора, но в общем случае, если они считываются из одного и того же источника (локального, поля, константы, встроенного в коде, результате функции и т. д.) требуется ровно столько же тактов.


1

Вот небольшая демонстрация использования кодов возврата ...

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

Это пример того, как проверить, как работает grep.

Когда вы выполняете команду 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 возвращать 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

Как правильно (наиболее эффективно) определить функцию main () в 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.