Ответ 1
Текущий стандарт на момент этого ответа (C11) явно упоминает эти два:
int main(void);
int main(int argc, char* argv[]);
хотя он упоминает фразу "или эквивалент" со следующей сноской:
Таким образом, int
может быть заменено именем typedef
определенным как int
, или тип argv
может быть записан как char ** argv
и так далее.
Кроме того, он также предоставляет больше (определенных реализацией) возможностей.
В соответствующем разделе (раздел 5.1.2.2.1 раздела C11, но этот конкретный аспект не отличается от раздела C99) говорится:
Функция, вызываемая при запуске программы, называется main
. Реализация не объявляет прототип для этой функции. Он должен быть определен с типом возврата int
и без параметров:
int main(void) { /* ... */ }
или с двумя параметрами (именуемыми здесь как argc
и argv
, хотя могут использоваться любые имена, поскольку они являются локальными для функции, в которой они объявлены):
int main(int argc, char *argv[]) { /* ... */ }
или эквивалент; или каким-либо другим способом, определяемым реализацией.
Если они объявлены, параметры main
функции должны подчиняться следующим ограничениям:
-
Значение argc
должно быть неотрицательным.
-
argv[argc]
должен быть нулевым указателем.
-
Если значение argc
больше нуля, члены массива от argv[0]
до argv[argc-1]
включительно должны содержать указатели на строки, которым перед установкой программы передаются значения, определяемые реализацией средой хоста. Намерение состоит в том, чтобы предоставить программе информацию, определенную до ее запуска, из другого места в размещенной среде. Если среда хоста не способна снабжать строки буквами как в верхнем, так и в нижнем регистре, реализация должна обеспечивать получение строк в нижнем регистре.
-
Если значение argc
больше нуля, строка, на которую указывает argv[0]
представляет имя программы; argv[0][0]
должен быть нулевым символом, если имя программы недоступно из среды хоста. Если значение argc
больше единицы, строки, на которые указывают argv[1]
argv[argc-1]
представляют параметры программы.
-
Параметры argc
и argv
и строки, на которые указывает массив argv
должны изменяться программой и сохранять свои последние сохраненные значения между запуском программы и ее завершением.
Обратите внимание, что это для размещенной среды, которую вы обычно видите в программах на Си. Автономная среда (такая как встроенная система) гораздо менее ограничена, как указано в 5.1.2.1 того же стандарта:
В автономной среде (в которой выполнение программы на С может происходить без какой-либо выгоды от операционной системы), имя и тип функции, вызываемой при запуске программы, определяются реализацией. Любые библиотечные средства, доступные для автономной программы, кроме минимального набора, требуемого в разделе 4, определяются реализацией.
Ответ 2
Стандарт С
Для размещенной среды (это нормальная среда) стандарт C99 гласит:
5.1.2.2.1 Запуск программы
Функция, вызываемая при запуске программы, называется main
. Реализация не объявляет прототип для этой функции. Он должен быть определен с типом возврата int
и без параметров:
int main(void) { /* ... */ }
или с двумя параметрами (именуемыми здесь как argc
и argv
, хотя могут использоваться любые имена, поскольку они являются локальными для функции, в которой они объявлены):
int main(int argc, char *argv[]) { /* ... */ }
или эквивалент; 9) или каким-либо другим способом, определяемым реализацией.
9) Таким образом, int
может быть заменено именем typedef, определенным как int
, или тип argv
может быть записан как char **argv
, и так далее.
Стандарты C11 и C18, по сути, соответствуют стандарту C99.
Стандарт C++
Стандарт C++ 98 гласит:
3.6.1 Основная функция [basic.start.main]
1 Программа должна содержать глобальную функцию main, которая является назначенным началом программы. [...]
2 Реализация не должна предопределять основную функцию. Эта функция не должна быть перегружена. Он должен иметь возвращаемый тип типа int, но в противном случае его тип определяется реализацией. Все реализации должны позволять оба следующих определения main:
int main() { /* ... */ }
а также
int main(int argc, char* argv[]) { /* ... */ }
Стандарт C++ прямо говорит: "Он [основная функция] должен иметь тип возвращаемого значения типа int, но в противном случае его тип определяется реализацией", и для него требуются те же две подписи, что и для стандарта C. Таким образом, void main() напрямую не разрешен стандартом C++, хотя он ничего не может сделать, чтобы не допустить, чтобы нестандартная соответствующая реализация не позволяла альтернативы (или стандартная соответствующая реализация не позволяла альтернативы в качестве расширений стандарта).
Стандарты C++ 03, C++ 11, C++ 14 и C++ 17 говорят, по сути, о том же, что и C++ 98.
Общее расширение
Классически Unix-системы поддерживают третий вариант:
int main(int argc, char **argv, char **envp) { ... }
Третий аргумент представляет собой список указателей на строки с нулевым символом в конце, каждый из которых является переменной окружения, которая имеет имя, знак равенства и значение (возможно, пустое). Если вы не используете это, вы все равно можете получить доступ к окружению через ' extern char **environ;
". Долгое время у этого не было заголовка, который объявил его, но стандарт POSIX 2008 теперь требует, чтобы он был объявлен в <unistd.h>
.
Это признано стандартом C как общее расширение, документированное в Приложении J:
J.5.1 Аргументы среды
In1 В размещенной среде основная функция получает третий аргумент char *envp[]
, который указывает на массив указателей на char
с нулевым символом в 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
.
Мне не ясно, что происходит (какой код выхода возвращается родителю или o/s), когда программа с void main()
завершает работу - и веб-сайт MS тоже молчит.
Интересно, что MS не предписывает версию main()
с двумя аргументами, которая требуется для стандартов C и C++. Он только предписывает форму с тремя аргументами, где третий аргумент - char **envp
, указатель на список переменных среды.
На странице Microsoft также перечислены некоторые другие альтернативы - wmain()
которая принимает строки широких символов, и некоторые другие.
Версия этой страницы для Microsoft VS 2005 не содержит void main()
в качестве альтернативы. Версии от Microsoft VS 2008 года и старше делают.
Является ли int main()
таким же, как int main(void)
?
Для подробного анализа смотрите конец моего ответа на Что должно возвращать main()
в C и C++. (Похоже, я когда-то считал, что этот вопрос относится к C++, хотя он этого не делал и никогда не делал. В C++ нет разницы между int main()
и int main(void)
и int main()
является идиоматическим C++.)
В C есть разница между двумя обозначениями, но вы заметите это только в эзотерических случаях. В частности, есть разница, если вы вызываете функцию main()
из своего собственного кода, что вам разрешено делать в C и не разрешено делать в C++.
Нотация int main()
не предоставляет прототипа для main()
, но это имеет значение, только если вы вызываете его рекурсивно. С помощью int main()
вы можете позже (в той же функции или в другой функции) написать int rc = main("absolute", "twaddle", 2):
и формально компилятор не должен жаловаться до степени отказа чтобы скомпилировать код, хотя он может на законных основаниях жаловаться (предупредить вас) (а использование -Werror
с GCC преобразует предупреждение в ошибку). Если вы используете int main(void)
, последующий вызов main()
должен выдать ошибку - вы сказали, что функция не принимает аргументов, но пыталась предоставить три. Конечно, вы не можете законно вызывать main()
до того, как объявите или определили его (если только вы не используете семантику C90) - и реализация не объявляет прототип для main()
. NB. Стандарт C11 иллюстрирует как int main()
и int main(void)
в разных примерах - оба действительны в C, хотя между ними есть небольшая разница.