Каковы действительные сигнатуры для функции C main()?

Каковы действительные сигнатуры для основной функции в C? Я знаю:

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

Существуют ли другие допустимые?

Ответы

Ответ 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, хотя между ними есть небольшая разница.

Ответ 3

POSIX поддерживает execve(), который, в свою очередь, поддерживает

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

Добавленный аргумент - это среда, то есть массив строк NAME = VALUE.

Ответ 4

http://en.wikipedia.org/wiki/Main_function_(programming)#C_and_C.2B.2B

Помимо обычных int main(int argc, char *argv[]) и POSIX int main(int argc, char **argv, char **envp), в Mac OS X также поддерживается

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

Конечно, это только для Mac.

В Windows есть

int wmain(int argc, wchar_t* argv[], wchar_t* envp[]);

как вариант Unicode (фактически, широко-символьный). Конечно, есть и WinMain.

Ответ 5

int main(void)

В некоторых ОС (например, Windows) также допустимо:

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

где envp предоставляет среду, в противном случае доступную через getenv()