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

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

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

Есть ли другие действующие?


person Prady    schedule 21.01.2010    source источник
comment
Какая версия C? Старые версии компиляторов позволяют все.   -  person Mr. Boy    schedule 21.01.2010
comment
ОП должен четко указать, что он имеет в виду под загадочным прозвищем C. Стандартный C? Какой стандарт C?   -  person mloskot    schedule 21.01.2010
comment
Я склонен предполагать, что когда кто-то говорит о C, они имеют в виду ISO C. Если они опускают версию, я предполагаю, что это текущий C99, но все же даю информацию о c1x, если это актуально.   -  person paxdiablo    schedule 21.01.2010
comment
В сентябре 2013 года этот вопрос был закрыт как дубликат Что должно возвращать main() на C и C ++?, но он был вновь открыт в июле 2017 года после почти 5-летнего перерыва. Информация, содержащаяся в ответах, повторяется в ответах на этот вопрос.   -  person Jonathan Leffler    schedule 14.09.2018
comment
Есть также еще один вопрос, который когда-то дублировался: Какое правильное объявление of main()?, хотя он был создан после этого вопроса и является строго вопросом C ++, поэтому его дублирование не совсем подходит.   -  person Jonathan Leffler    schedule 14.09.2018


Ответы (5)


В стандарте C11 прямо упоминаются эти два:

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

хотя в нем упоминается фраза или ее эквивалент со следующей сноской:

Таким образом, int можно заменить на typedef имя, определенное как int, или тип argv можно записать как char ** argv и так далее.

Кроме того, он также предоставляет больше возможностей (определяемых реализацией).

Соответствующий текст (раздел 5.1.2.2.1, но этот конкретный аспект не изменился с 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, должны изменяться программой и сохранять свои последние сохраненные значения между запуском программы и ее завершением.

Обратите внимание, что это для размещенной среды, той, которую вы обычно видите в программах на C. Автономная среда (например, встроенная система) гораздо менее ограничена, как указано в 5.1.2.1 того же стандарта:

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

person paxdiablo    schedule 21.01.2010
comment
Как насчет int main(int argc, const char* argv[]);? - person potrzebie; 15.08.2013
comment
@potrzebie Согласно стандарту, раздел 5.1.2.2.1: Параметры argc и argv и строки, на которые указывает массив argv, должны быть изменены программой [...]. Таким образом может показаться, что const в подписи недействительна. - person arvidj; 08.07.2014
comment
Чтобы ваш ответ был более надежным в будущем, укажите, пожалуйста, текущий стандарт. - person Cristian Ciupitu; 23.11.2014
comment
@ Christian, я упомянул об этом в вопросе, сославшись на текст в C11 и отметив, что C99 почти идентичен. Но я повторю (preiterate?) Это также в первом абзаце, согласно вашему предложению. Ваше здоровье. - person paxdiablo; 24.11.2014
comment
@potrzebie Вероятно, можно было бы защитить int main(int argc, char* const argv[]){...}, сделав сам параметр параметром константным. Я бы подумал, что это оправданно, потому что оно совместимо с предполагаемым неявным объявлением int main(int argc, char* argv[]);. - person Peter - Reinstate Monica; 19.06.2019
comment
@Peter, я не полностью уверен, что это будет действительным в соответствии с или аналогичным правилом. Поскольку неконстантный вариант позволит вам изменить argv[3], чтобы он указывал на совершенно другую строку, эта возможность теряется, если вы const отдельные указатели. Я подозреваю, что это не будет отличаться от const char *argv[], который лишит вас возможности изменять символы с помощью аргументов (например, argv[3][1] = '\0', чтобы заставить его использовать только первый символ). - person paxdiablo; 20.06.2019
comment
@Peter, в частности, текст argc и argv и строки, на которые указывает массив argv, должны быть изменены программой, которая может вступить в игру, хотя ее можно читать только как argv и символы, указывающие на by argv[N], не отдельные указатели, составляющие argv. Поэтому в стандартах нет места неряшливой лексике :-) - person paxdiablo; 20.06.2019
comment
@paxdiablo Я думаю, мы согласны с тем, что argv (собственно), безусловно, можно изменять (поскольку он, как и все параметры функции, является локальной копией), но функция реализация может свободно заявить, что она воздерживается от изменения этого локальная переменная, объявив ее константой, без изменения сигнатуры функции. Стандарт означает, что argv не указывает на константную память, как и указатели в этой неконстантной памяти (то есть, что мы можем сказать ++argv, само собой разумеется, но стандарт требует, чтобы мы также могли сказать ++*argv и даже ++**argv (если argc ›0). - person Peter - Reinstate Monica; 20.06.2019

Стандарт C

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

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

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

¶1 В размещенной среде основная функция получает третий аргумент, 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.

Мне неясно, что происходит (какой код выхода возвращается родительскому элементу или 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, хотя между ними есть небольшая разница.

person Jonathan Leffler    schedule 25.11.2010
comment
но это имеет значение только в том случае, если вы вызываете его рекурсивно. Интересный факт: вызов main изнутри программы - это UB в C ++; компиляторам разрешено вставлять вызовы статических конструкторов в начало main или что-то еще. Я думаю, что это разрешено в Си. (Но вы можете обнаружить, что ICC сбрасывает режимы среды / округления FP, потому что он вызывает функцию Intel init из верхней части main.) - person Peter Cordes; 19.06.2019
comment
Очень сложно найти именно в C11 текст, который позволяет вам сказать (выделенная жирным шрифтом фраза): NB: Стандарт C11 иллюстрирует как int main (), так и int main (void) в разных примерах - оба действительны в C,. Я действительно вижу этот точный синтаксис в C11, но не нашел содержимого, в котором прямо указано, что он действителен. Но тогда у меня нет опыта в правильной интерпретации того, что я вижу в стандарте. Должны ли мы интерпретировать то, что, если конкретный синтаксис существует в качестве иллюстрации в этом стандарте, он должен считаться законным (или действительным)? - person ryyker; 16.09.2020
comment
Найти информацию непросто, @ryyker, но в C11 §6.7.6 Declarators показывает, что для функции действителен пустой список аргументов, а §6.9.1 Определения функций использует нотацию declarator из §6.7.6. Противодействием общему материалу "определение функции" является §5.1 .2.2.1 Запуск программы, показанный в этом ответе. Предложение или эквивалентное ему предложение и его сноска неоднозначны, но int main() { … } эквивалентно int main(void) { … }. - person Jonathan Leffler; 16.09.2020
comment
Обратите внимание, что int main(); и int main(void); не эквивалентны. Оба объявляют (а не определяют) функцию, но в первом ничего не указывается в списке параметров (не предоставляется прототип функции), а во втором явно говорится, что функция не принимает аргументов. (Разница между объявлением и определением заключается в том, почему в предыдущем комментарии есть int main() { … }, обозначающее определение функции, тогда как здесь { … } заменено точкой с запятой, обозначающей объявление функции.) - person Jonathan Leffler; 16.09.2020
comment
Спасибо, Джонатан. Ваша ясность в раскручивании сложного контента очень помогает. Мне действительно интересно, хотя в последнем утверждении вашего первого ответа вы говорите: но int main() { … } эквивалентно int main(void) { … }. Затем в первом утверждении во втором комментарии вы, кажется, делаете противоположное утверждение: Обратите внимание, что int main(); и int main(void); не эквивалентны. Я считаю, что в целом я истолковал, что эти два не эквивалентны, поскольку один (int main();) может быть продемонстрирован для размещения нескольких аргументов, а другой не сможет скомпилировать ни с какими аргументами. - person ryyker; 16.09.2020
comment
Хм - да, строго говоря, int main() { … } и int main(void) { … } не эквивалентны, потому что первый все еще не предоставляет прототип для main(), тогда как последний делает. Оба, однако, определяют функцию, которая не принимает аргументов (и в этом смысле они эквивалентны - это то, что я должен был сказать, но в комментарии не хватало места). Единственный раз, когда разница имеет значение, это если ваш код вызывает main() рекурсивно (или беспорядок с указателями функций на main()) - ни то, ни другое не является повседневным занятием для программистов C (а рекурсивные вызовы запрещены в C ++). - person Jonathan Leffler; 16.09.2020
comment
Эти комментарии (и ответ) проливают очень полезный свет на иногда спорную тему. У меня теперь он помечен как ссылка :) Еще раз спасибо. - person ryyker; 16.09.2020
comment
@ryyker Я считаю, что этот случай рассматривается в §6.7.6 Деклараторы, семантика, p14 ... Пустой список в деклараторе функции, который является частью определения этой функции, указывает, что функция не имеет параметров ... с примечанием, 145, которое приводит к 6.11.6: Использование деклараторов функций с пустые круглые скобки (не деклараторы типов параметров в формате прототипа) являются устаревшей функцией.. - person Bob__; 16.09.2020
comment
Я просто могу сказать СПАСИБО. Это очень исчерпывающее объяснение, особенно. относительно общих возможных расширений третьего аргумента. Если бы я мог, я бы проголосовал за это дважды! - person math; 10.03.2021

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

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

Добавленный аргумент - это окружение, то есть массив строк в форме ИМЯ = ЗНАЧЕНИЕ.

person unwind    schedule 21.01.2010
comment
Это не совсем так. Execve принимает аргумент среды, но это не имеет ничего общего с соглашением о вызовах для main. Скорее он используется для инициализации extern char **environ;. - person R.. GitHub STOP HELPING ICE; 02.06.2011
comment
@R ..: На практике многие реализации C в системах POSIX действительно передают третий envp аргумент main. Я не уверен, указывает ли сам POSIX это как 3-ю действительную подпись для main или нет. Вы можете убедиться, что это работает на практике в GNU C с помощью этой программы: godbolt.org/z/9lie95 (он передает свои argv и envp в execve("/usr/bin/env"), поэтому вы можете видеть, что он унаследовал нормальную среду вместо того, чтобы возвращать -EFAULT). Но да, этот ответ описывает это неправильно, подразумевая, что существование execve подразумевает новую подпись для main. - person Peter Cordes; 19.06.2019

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.

person kennytm    schedule 21.01.2010

int main(void)

Под некоторыми ОС (например, Windows) действительно также такое:

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

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

person flashnik    schedule 21.01.2010