Каков правильный (наиболее эффективный) способ определения функции main() в C и C++ - int main() или void main() - и почему? А как насчет аргументов?
Если int main(), то return 1 или return 0?
Есть множество дубликатов этого вопроса, в том числе:
main()?main()void main() и int main()?main() на C++main()? - Для C++ действительно очень хороший ответ.main() в Cmain() в Cint main() против void main() в CСвязанный:
int main(int argc, char **argv)int main(int argc, char *argv[])char *envp[] третьим аргументом в пользу портативности main()?int main() возвращать значение во всех компиляторах?main() в C и C++ предоставляется пользователю определять?int main(){}?main() в C++ 14?Pish posh, контекст эффективного здесь очевиден, особенно с примерами (которые, вероятно, там проясняют определение «эффективного»). Будем надеяться, что плохой буфер не залез в яму и полностью сожалею о своем вопросе. Можно сказать, что независимо от void или int возвращается значение, поэтому оно не влияет на размер файла, выполняемые операции или выделенную память. И люди в большинстве операционных систем, как правило, возвращают 0 в случае успеха и что-то еще в случае успеха или неудачи, но стандарта нет. В конечном счете, никакой очевидной разницы в эффективности.
«правильный (наиболее эффективный)» не имеет смысла. Эффективность - это одно, а правильность - другое. main вызывается один раз (а в C++ может вызываться только один раз: без рекурсии). Если вы не хотите, чтобы выполнение в main занимало много времени, не вызывайте программу много раз: пусть программа реализует повторение.
Мне интересно, что ни один из ответов, насколько я могу судить, не дает полностью рабочего примера, включая утверждения #include.
@puk: нет необходимости в «полностью рабочем примере», и для записи main() не нужны заголовки - даже если это будет необычная программа, для которой не нужны некоторые заголовки.
То, что здесь до сих пор не упоминается ни в одном из ответов, заключается в том, что глава о типе возврата main, цитируемая повсюду, представляет собой подраздел из глава о размещенной среде, который, в свою очередь, является подраздел соответствующей главы Среда выполнения. Настоящие ответы на данный момент охватывают только 50% стандарта. Итак, я написал ответ, который нацелен на 100% ответ на вопрос.
Также по теме: Что послужило основанием для того, чтобы сделать return 0 в конце main необязательным?
Этот questin может быть расширен с помощью What is the Standard и порекомендовать способ использования main() в программах C.
Возвращаемые значения не имеют смысла на платформе без ОС. Вы ни к чему не вернетесь. Если вы нажмете return в main(...) на встроенном устройстве, ваша система перейдет в непредсказуемое состояние, и ваша стиральная машина осознает себя и попытается убить вас. Итак, в этом случае мы используем void main(). Это стандартная практика для встраиваемых систем с голым металлом.





Возвращаемое значение main указывает, как программа завершилась. Нормальный выход представлен возвращаемым значением 0 от main. Аномальный выход сигнализируется ненулевым возвратом, но нет стандарта для интерпретации ненулевых кодов. Как отмечали другие, void main() запрещен стандартом C++ и не должен использоваться. Допустимые подписи C++ main:
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() разрешен, но этого следует избегать.
main МОЖНО вводить / выходить несколько раз, но эта программа, вероятно, не выиграет никаких дизайнерских наград;)
C99 также имеет неправильную функцию C++, заключающуюся в том, что достижение конца функции main () эквивалентно возврату 0 - если main () определен для возврата типа, совместимого с int (раздел 5.1.2.2.3).
повторный вход в main не является допустимым C++. В стандарте 3.6.1.3 явно указано, что main не должен использоваться в программе.
stdlib.h предоставляет для этой цели EXIT_SUCCESS и EXIT_FAILURE
@korona: использование main () изнутри кода - неопределенное поведение. Поскольку компилятор может (в зависимости от компилятора) вставить код для обработки инициализации программы на этом этапе. Дважды запускать код инициализации может быть не самой лучшей идеей.
0 и ненулевое значение верны, но совершенно бессмысленны для того, кто читает ваш код. Этот вопрос является доказательством того, что люди не знают, что такое действительные / недействительные коды. EXIT_SUCCESS / EXIT_FAILURE гораздо понятнее.
JaredPar: возврат 0 из main четко определен в C как успешное завершение. Мартин Йорк: вызов main () изнутри программы на языке C, безусловно, является неопределенным поведением нет. Я не могу сказать то же самое о C++.
Как уже говорилось, для C++ вызов main () явно указан в стандарте как недопустимый (что на шаг хуже, чем undefined). Однако для C случай, очевидно, другой (но, вероятно, лучше избегать;))
Это может быть четко определено в стандарте C, но это определенно не совсем общеизвестно. В противном случае этот вопрос не был бы опубликован.
JaredPar: ну, это общеизвестно среди программистов, которые знают C, и обычно я ожидаю, что люди, читающие исходный код C, поймут C.
@ workmad3, обратите внимание на этот отчет о дефектах об использовании main для GCC: gcc.gnu.org/bugzilla/show_bug.cgi?id=41431. Также обратите внимание на следующие допустимые определения main: groups.google.com/group/comp.std.c++/browse_thread/thread/…
Гарантируется ли, что возвращаемое значение основной функции будет кодом выхода из программы? Я всегда использовал функцию exit в своих программах, чтобы установить код выхода, отличный от 0.
@ Калмарий: Да. Раздел 5.1.2.2.3 стандарта говорит: «... возврат из первоначального вызова функции main эквивалентен вызову функции exit со значением, возвращаемым функцией main в качестве аргумента ...»
Каждый разработчик на C знает, что код выхода INT 0 означает успех, так зачем же так много тонуть в стакане воды?
Этот ответ охватывает только 50% стандартов, поскольку в нем не упоминаются автономные реализации. "void main () явно запрещена стандартом C++" ... необходима цитата (вы ее не найдете, она скорее неявно запрещает void main () для размещенных систем).
@Lundin main() упоминается только для размещенных форм C и C++. Таким образом, неявно вопрос касается размещенных C и C++.
Технически подпись должна быть unsigned char main(...), потому что подавляющее большинство сред оболочки требует, чтобы вы возвращали значение от 0 до 255.
@ Majora320: не совсем. Стандарты говорят int и означают int. Однако вы правы, что только 8 младших битов int используются в качестве статуса выхода. См. Также Коды выхода больше 255 - возможно?.
'явно запрещен стандартом C++ и не должен использоваться' А также C. Даже если 'это работает', он сломан. Предположительно поэтому то же самое и в C++, если вы не имеете в виду, что он не будет компилироваться? Я не фанат C++, но в любом случае я знаю лучше, чем писать void main (), чтобы не столкнуться с проблемой.
Вы забыли упомянуть int main(void); как действительную подпись в C. В C int main(); действительно означает int main(...). Только в C++ int main() означает int main(void). (Вот почему плохо, что вы говорили только о сигнатурах C++ для него)
Ноль / ненулевое значение для сообщения об успехе / ошибке не соответствует действительности на всех платформах (например, VMS). В переносимом коде следует использовать макросы EXIT_SUCCESS / EXIT_FAILURE.
Дайте ссылку на "void main () явно запрещена стандартом C++", так как я не нахожу такого запрета в basic.start.main. Пока поддерживаются стандартные подписи, реализация может поддерживать любое количество нестандартных подписей. (Тем не менее, я бы сильно посоветовал бы не использовать такое расширение, даже если оно доступно).
@TobySpeight: я бы использовал void main() во встроенной системе, где main не возвращается. У нас есть лучший способ сказать это сейчас, но встроенные компиляторы уже позади.
Возвращает 0 в случае успеха и ненулевое значение в случае ошибки. Это стандарт, используемый сценариями UNIX и DOS для выяснения того, что произошло с вашей программой.
Я считаю, что main() должен возвращать либо EXIT_SUCCESS, либо EXIT_FAILURE. Они определены в stdlib.h.
@ChrisYoung Есть EXIT_SUCCESS и EXIT_FAILURE, потому что в некоторых старых операционных системах (VMS?) Для обозначения успеха использовалось число, отличное от 0. Сейчас везде 0.
@FUZxxl, вы правы, но это не противоречит моему комментарию. EXIT_SUCCESS действительно может быть ненулевым, но все стандарты (C89, C99, C11) определяют 0 (а также EXIT_SUCCESS), чтобы также быть определяемой реализацией формой успешного завершения статуса.
@ChrisYoung Спасибо, что поправили меня. Видимо, я ошибался.
Хотя этот комментарий правильный и ценный, он не отвечает на вопрос.
@FUZxxl: это правда, что VMS использовала нечетные значения (например, 1) для обозначения успеха и четные значения (например, 0) для обозначения неудачи. К сожалению, исходный стандарт ANSI C интерпретировался как означающий, что EXIT_SUCCESS должен быть 0, поэтому возвращение EXIT_SUCCESS из main приводило к совершенно неправильному поведению в VMS. Портативным решением для VMS было использование exit(EXIT_SUCCESS), который всегда поступал правильно.
5.1.2.2.3 "Если тип возврата основной функции является типом, совместимым с int, возврат из первоначального вызова к основной функции эквивалентен вызову функции выхода со значением, возвращаемым основной функцией в качестве аргумента; 11) достижение}, завершающего основную функцию, возвращает значение 0. "
А потом 7.22.4.4. о функции выхода: «Если значение статуса равно нулю или EXIT_SUCCESS, возвращается форма успешного завершения статуса, определяемая реализацией. Если значением статуса является EXIT_FAILURE, возвращается форма, определяемая реализацией неудачного завершения статуса. В противном случае возвращаемый статус определяется реализацией ".
У меня сложилось впечатление, что стандарт указывает, что main не требует возвращаемого значения, поскольку успешный возврат основан на ОС (ноль в одном может быть либо успехом, либо неудачей в другом), поэтому отсутствие возврата было сигналом для компилятор для вставки самого успешного возврата.
Однако обычно я возвращаю 0.
C99 (и C++ 98) позволяют опускать оператор return из main; C89 не позволяет вам опускать оператор возврата.
Это комментарий, а не ответ.
Это не дает ответа на вопрос. Чтобы критиковать или запрашивать разъяснения у автора, оставьте комментарий под его сообщением.
@SteveLillis: В 2008 году у SO не было раздела для комментариев.
Имейте в виду, что даже если вы возвращаете int, некоторые операционные системы (Windows) усекают возвращаемое значение до одного байта (0–255).
Unix делает то же самое, вероятно, и большинство других операционных систем. Я знаю, что VMS делает с ним такие невероятные странные вещи, что возвращение чего-либо, кроме EXIT_SUCCESS или EXIT_FAILURE, вызывает проблемы.
MSDN требует отличия: когда сообщается через mscorlib, код выхода - 32-битное целое число со знаком. Похоже, это означает, что Библиотеки времени выполнения C, который усекает коды выхода, неисправен.
Да, это неверно. В Windows возвращается 32-битное целое число (и преобразуется в unsigned). То же самое в системах UNIX с 32-битными целыми числами. Но оболочки в стиле UNIX в любой системе обычно сохраняют только 8-битное целое число без знака.
Принятый ответ, похоже, предназначен для C++, поэтому я подумал, что добавлю ответ, относящийся к C, и это несколько отличается. Также были внесены некоторые изменения между ISO / IEC 9899: 1989 (C90) и ISO / IEC 9899: 1999 (C99).
main() должен быть объявлен как:
int main(void)
int main(int argc, char **argv)
Или эквивалент. Например, int main(int argc, char *argv[]) эквивалентен второму. В C90 тип возврата int может быть опущен, поскольку он является значением по умолчанию, но в C99 и новее тип возврата int не может быть пропущен.
Если реализация позволяет это, main() может быть объявлен другими способами (например, int main(int argc, char *argv[], char *envp[])), но это делает реализацию программы определенной и больше не соответствует строго.
Стандарт определяет 3 значения для возврата, которые строго соответствуют (то есть не зависят от поведения, определенного реализацией): 0 и EXIT_SUCCESS для успешного завершения и EXIT_FAILURE для неудачного завершения. Любые другие значения являются нестандартными и определяются реализацией. В C90 main() должен иметь явный оператор return в конце, чтобы избежать неопределенного поведения. В C99 и новее вы можете опустить оператор возврата из main(). Если вы это сделаете, и main() закончил, есть неявный return 0.
Наконец, с точки зрения стандартов, нет ничего плохого в вызове main()рекурсивно из программы на языке C.
@ Крис Янг: Куда денется возвращаемое значение. И какой толк от возвращенного значения? Вы можете это объяснить. или просто чтобы избежать неопределенного поведения.
@ MELWIN: возвращаемое значение передается в среду хоста. В большинстве случаев это будет ваша ОС, которая может передать это значение другому запущенному процессу по запросу (например, вызову system ()).
Что касается «Стандарт определяет 3 значения для возврата ... 0, EXIT_SUCCESS, EXIT_FAILURE», действительно ли «0, EXIT_SUCCESS, EXIT_FAILURE» является частью спецификаций ISO-C и C++? Если да, то где в спецификациях?
C11 §7.22p3 и §7.22.4.4p5. C++ 11 ссылается на стандартную библиотеку C, но они кратко упомянуты в §18.5p3.
«Если реализация позволяет это, main может быть объявлен другими способами, но это делает реализацию программы определенной и более не строго соответствующей». Требуется цитата.
@Lundin Я не думаю, что вам нужна цитата, чтобы сказать, что кому-то разрешено создавать компилятор, который принимает нестандартные программы, или иметь компилятор, не соответствующий стандартам. Это общеизвестно и здравый смысл
@KABoissonneault Поведение, определяемое реализацией, - это термин из стандарта, в отличие от полностью недокументированного поведения. Если вы реализуете что-то, что указано как поведение, определяемое реализацией, вы по-прежнему следуете стандарту. В этом случае C89, который был процитирован, не перечисляет такое поведение, определяемое реализацией, поэтому необходимо цитировать, чтобы доказать, что он не просто выдумывает что-то на ровном месте.
@Lundin Ты неправильно понимаешь. То, о чем мы говорим, не является поведением, определяемым реализацией, мы говорим о реализации, отклоняющейся от стандарта, если они того пожелают. Это больше похоже на то, как ребенок не слушается своих родителей: вам не нужна цитата из родителей, чтобы сказать вам, каким образом ребенок может пойти против того, что говорят родители. Вы просто знаете, что в тот момент, когда ребенок решает это сделать, он больше не соблюдает гильдена своих родителей.
@KABoissonneault Часть, которую я процитировал в своем комментарии, определенно касается поведения, определяемого реализацией (в отличие от нестандартные расширения компилятора). Таким образом, я говорю о поведении, определяемом реализацией. Если у вас есть монолог о другом, удачи вам с этим.
@Lundin Я полагаю, что формулировка в цитате сбивает с толку (часть, где говорится «но это определяет реализацию программы»), но я почти уверен, что этот человек говорил о нестандартном поведении (как сказано в «Если выполнение разрешает это «и» и больше не соответствует [стандарту] ») в отличие от поведения, определяемого реальной реализацией. Человеку обязательно стоит перефразировать свой ответ, но я все же не думаю, что для этого нужна цитата из стандарта.
«но это делает реализацию программы определенной, а не строгим соответствием». -> Код все еще соответствует. Конечно, менее вероятно с сигнатурой, определенной реализацией.
return 0 will be automatically generated by the compiler. No UB is triggered in this case. Please see answers below for quotations from the standard.
@programmersn часть, которую вы цитируете в моем ответе, касается C90. Если вы продолжите читать часть ниже (C99), вы увидите, что я говорю, что вы можете опустить возврат из main (), поскольку есть неявный возврат 0. Это потому, что это было добавлено в стандарт только с C99.
@Chris: Плохо, что я не увидел этой части ответа, сразу сделал вывод. Не могли бы вы внести какие-либо изменения в ответ, чтобы я мог отозвать отрицательный голос. В любом случае спасибо за разъяснения
Если у вас действительно есть проблемы, связанные с эффективностью возврата целого числа из процесса, вам, вероятно, следует избегать вызова этого процесса столько раз, что это возвращаемое значение станет проблемой.
Если вы делаете это (вызываете процесс столько раз), вы должны найти способ поместить свою логику непосредственно внутри вызывающего объекта или в файл DLL, не выделяя конкретный процесс для каждого вызова; распределение нескольких процессов создает в этом случае соответствующую проблему эффективности.
В деталях, если вы хотите знать, является ли возврат 0 более или менее эффективным, чем возврат 1, в некоторых случаях это может зависеть от компилятора, но в целом, если они считываются из одного и того же источника (локального, полевого, константы, встроенного в коде, результате функции и т. д.) требуется точно такое же количество тактов.
Что нужно вернуть, зависит от того, что вы хотите делать с исполняемым файлом. Например, если вы используете свою программу с оболочкой командной строки, вам нужно вернуть 0 в случае успеха и ненулевое значение в случае неудачи. Тогда вы сможете использовать программу в оболочках с условной обработкой в зависимости от результата вашего кода. Также вы можете присвоить любое ненулевое значение в соответствии с вашей интерпретацией, например, для критических ошибок разные точки выхода программы могут завершить программу с разными значениями выхода, и это доступно для вызывающей оболочки, которая может решить, что делать, проверив возвращенное значение.
Если код не предназначен для использования с оболочками и возвращаемое значение никого не беспокоит, его можно пропустить. Лично пользуюсь подписью int main (void) { .. return 0; .. }
Формат main () определяется реализацией, то есть компилятором. Программист не может выбрать, какую форму выбрать, за исключением случаев, когда компилятор поддерживает несколько форм.
@Lundin Тип возвращаемого значения будет реализацией реализации. Но значение, которое нужно вернуть, решает программист. В разделе 5.1.2.2.3 C99 упоминается, что возвращаемый тип main совместим с int. Поэтому вернуть int не составит труда. Хотя другие типы возвращаемых значений разрешены, но в этом случае переменная среды, имеющая возвращаемое значение, не будет указана. Но если программист делает return 0;, то в bash его можно использовать для создания веток.
Возвращаемое значение может использоваться операционной системой для проверки того, как была закрыта программа.
Возвращаемое значение 0 обычно означает ОК в большинстве операционных систем (тех, о которых я все равно могу думать).
Это также можно проверить, когда вы сами вызываете процесс, и посмотрите, завершилась ли программа и завершилась ли она должным образом.
Это НЕТ просто соглашение о программировании.
В вопросе нет ничего, что указывало бы на наличие операционной системы. В автономной системе возвращать значение не имеет смысла.
Возвращаемое значение main() показывает, как программа завершилась. Если возвращаемое значение - zero, это означает, что выполнение было успешным, в то время как любое ненулевое значение будет означать, что что-то пошло не так при выполнении.
Это комментарий, а не ответ на вопрос.
main() в C89 и неопределенные типы возвращаемых значений K&R C по умолчанию имеют значение 'int`.
return 1? return 0?
Если вы не напишите оператор возврата в int main(), закрывающий { по умолчанию вернет 0.
return 0 или return 1 будет получен родительским процессом. В оболочке он переходит в переменную оболочки, и если вы запускаете свою программу из оболочки и не используете эту переменную, вам не нужно беспокоиться о возвращаемом значении main().
См. Как я могу получить то, что вернула моя основная функция?.
$ ./a.out
$ echo $?
Таким образом, вы можете увидеть, что именно переменная $? получает младший байт возвращаемого значения main().
В сценариях Unix и DOS обычно возвращается return 0 в случае успеха и ненулевое значение в случае ошибки. Это стандарт, используемый сценариями Unix и DOS, чтобы узнать, что произошло с вашей программой, и контролировать весь поток.
Строго говоря, $? не является переменной среды; это предопределенная (или встроенная) переменная оболочки. Разницу трудно заметить, но если вы запустите env (без аргументов), он распечатает среду, и $? не будет отображаться в среде.
Автоматическое возвращение 0, когда основное "падает до конца" только в C++ и C99 и далее, а не в C90.
Опечатка: «закрытие {» должно быть }. ТАК не позволит мне сделать такое маленькое редактирование.
Возвращение 0 должно сообщить программисту, что программа успешно завершила работу.
Возвращение 1 из main() обычно сигнализирует об ошибке; возврат 0 сигнализирует об успехе. Если ваши программы всегда терпят неудачу, то 1 в порядке, но это не лучшая идея.
@JonathanLeffler: Значение возврата 1 из main определяется реализацией. Единственными языковыми значениями являются 0, EXIT_SUCCESS (часто обозначаемый как 0) и EXIT_FAILURE. В OpenVMS return 1; обозначает завершение успешный.
VMS не «нормальный» - в смысле того, что я сказал. Разве это не что-то вроде «любая нечетная ценность - успех; даже значения сбой на VMS?
Для размещенной среды (это нормальная) стандарт C11 (ISO / IEC 9899: 2011) гласит:
5.1.2.2.1 Program startup
The function called at program startup is named
main. The implementation declares no prototype for this function. It shall be defined with a return type ofintand with no parameters:int main(void) { /* ... */ }or with two parameters (referred to here as
argcandargv, though any names may be used, as they are local to the function in which they are declared):int main(int argc, char *argv[]) { /* ... */ }or equivalent;10) or in some other implementation-defined manner.
If they are declared, the parameters to the main function shall obey the following constraints:
- The value of
argcshall be nonnegative.argv[argc]shall be a null pointer.- If the value of
argcis greater than zero, the array membersargv[0]throughargv[argc-1]inclusive shall contain pointers to strings, which are given implementation-defined values by the host environment prior to program startup. The intent is to supply to the program information determined prior to program startup from elsewhere in the hosted environment. If the host environment is not capable of supplying strings with letters in both uppercase and lowercase, the implementation shall ensure that the strings are received in lowercase.- If the value of
argcis greater than zero, the string pointed to byargv[0]represents the program name;argv[0][0]shall be the null character if the program name is not available from the host environment. If the value ofargcis greater than one, the strings pointed to byargv[1]throughargv[argc-1]represent the program parameters.- The parameters
argcandargvand the strings pointed to by theargvarray shall be modifiable by the program, and retain their last-stored values between program startup and program termination.10) Thus,
intcan be replaced by a typedef name defined asint, or the type ofargvcan be written aschar **argv, and so on.
Значение, возвращаемое main(), передается в «среду» способом, определяемым реализацией.
5.1.2.2.3 Program termination
1 If the return type of the
mainfunction is a type compatible withint, a return from the initial call to themainfunction is equivalent to calling theexitfunction with the value returned by themainfunction as its argument;11) reaching the}that terminates themainfunction returns a value of 0. If the return type is not compatible withint, the termination status returned to the host environment is unspecified.11) In accordance with 6.2.4, the lifetimes of objects with automatic storage duration declared in
mainwill have ended in the former case, even where they would not have in the latter.
Обратите внимание, что 0 считается «успешным». Вы можете использовать EXIT_FAILURE и EXIT_SUCCESS из <stdlib.h>, если хотите, но 0 хорошо установлен, так же как и 1. См. Также Коды выхода больше 255 - возможно?.
В C89 (и, следовательно, в Microsoft C) нет утверждения о том, что произойдет, если функция main() вернется, но не задает возвращаемое значение; поэтому это приводит к неопределенному поведению.
7.22.4.4 The
exitfunction¶5 Finally, control is returned to the host environment. If the value of
statusis zero orEXIT_SUCCESS, an implementation-defined form of the status successful termination is returned. If the value ofstatusisEXIT_FAILURE, an implementation-defined form of the status unsuccessful termination is returned. Otherwise the status returned is implementation-defined.
Стандарт C++ 11 (ISO / IEC 14882: 2011) гласит:
3.6.1 Main function [basic.start.main]
¶1 A program shall contain a global function called main, which is the designated start of the program. [...]
¶2 An implementation shall not predefine the main function. This function shall not be overloaded. It shall have a return type of type int, but otherwise its type is implementation defined. All implementations shall allow both of the following definitions of main:
int main() { /* ... */ }and
int main(int argc, char* argv[]) { /* ... */ }In the latter form
argcshall be the number of arguments passed to the program from the environment in which the program is run. Ifargcis nonzero these arguments shall be supplied inargv[0]throughargv[argc-1]as pointers to the initial characters of null-terminated multibyte strings (NTMBSs) (17.5.2.1.4.2) andargv[0]shall be the pointer to the initial character of a NTMBS that represents the name used to invoke the program or"". The value ofargcshall be non-negative. The value ofargv[argc]shall be 0. [ Note: It is recommended that any further (optional) parameters be added afterargv. —end note ]¶3 The function
mainshall not be used within a program. The linkage (3.5) ofmainis implementation-defined. [...]¶5 A return statement in main has the effect of leaving the main function (destroying any objects with automatic storage duration) and calling
std::exitwith the return value as the argument. If control reaches the end of main without encountering a return statement, the effect is that of executingreturn 0;
Стандарт C++ явно говорит: «Она [основная функция] должна иметь тип возвращаемого значения int, но в остальном ее тип определяется реализацией» и требует поддержки тех же двух сигнатур, что и стандарт C, в качестве параметров. Таким образом, void main () прямо не допускается стандартом C++, хотя он ничего не может сделать, чтобы остановить нестандартную реализацию, допускающую альтернативы. Обратите внимание, что C++ запрещает пользователю вызывать main (но стандарт C этого не делает).
В стандарте C++ 11 есть параграф §18.5 Начало и завершение, который идентичен параграфу из §7.22.4.4 Функция exit в стандарте C11 (цитируется выше), за исключением сноски (которая просто документирует, что EXIT_SUCCESS и EXIT_FAILURE определены в <cstdlib>).
Традиционно системы Unix поддерживают третий вариант:
int main(int argc, char **argv, char **envp) { ... }
Третий аргумент - это список указателей на строки с завершающим нулем, каждая из которых является переменной среды, имеющей имя, знак равенства и значение (возможно, пустое). Если вы не используете это, вы все равно можете получить доступ к среде через «extern char **environ;». Эта глобальная переменная уникальна среди переменных POSIX тем, что у нее нет заголовка, который ее объявляет.
Это признано стандартом C как общее расширение, задокументированное в Приложении J:
J.5.1 Environment arguments
¶1 In a hosted environment, the main function receives a third argument,
char *envp[], that points to a null-terminated array of pointers tochar, each of which points to a string that provides information about the environment for this execution of the program (5.1.2.2.1).
Компилятор Microsoft VS 2010 интересен. На сайте написано:
The declaration syntax for main is
int main();or, optionally,
int main(int argc, char *argv[], char *envp[]);Alternatively, the
mainandwmainfunctions can be declared as returningvoid(no return value). If you declaremainorwmainas returning void, you cannot return an exit code to the parent process or operating system by using a return statement. To return an exit code whenmainorwmainis declared asvoid, you must use theexitfunction.
Мне непонятно, что происходит (какой код выхода возвращается родительскому устройству или ОС), когда программа с void main() действительно завершается - и веб-сайт MS тоже молчит.
Интересно, что MS не предписывает версию main() с двумя аргументами, которая требуется стандартами C и C++. Он предписывает только форму с тремя аргументами, где третьим аргументом является char **envp, указатель на список переменных среды.
На странице Microsoft также перечислены некоторые другие альтернативы - wmain(), который принимает широкие строки символов, и некоторые другие.
Версия эта страница Microsoft Visual Studio 2005 не содержит void main() в качестве альтернативы. версии от Microsoft Visual Studio 2008 и далее делает.
Как отмечалось ранее, приведенные выше требования применяются к размещенным средам. Если вы работаете с автономной средой (которая является альтернативой размещенной среде), то стандарт может сказать гораздо меньше. Для автономной среды функцию, вызываемую при запуске программы, не нужно называть main, и нет ограничений на ее возвращаемый тип. В стандарте сказано:
5.1.2 Execution environments
Two execution environments are defined: freestanding and hosted. In both cases, program startup occurs when a designated C function is called by the execution environment. All objects with static storage duration shall be initialized (set to their initial values) before program startup. The manner and timing of such initialization are otherwise unspecified. Program termination returns control to the execution environment.
5.1.2.1 Freestanding environment
In a freestanding environment (in which C program execution may take place without any benefit of an operating system), the name and type of the function called at program startup are implementation-defined. Any library facilities available to a freestanding program, other than the minimal set required by clause 4, are implementation-defined.
The effect of program termination in a freestanding environment is implementation-defined.
Перекрестная ссылка на раздел 4 Соответствие относится к следующему:
¶5 A strictly conforming program shall use only those features of the language and library specified in this International Standard.3) It shall not produce output dependent on any unspecified, undefined, or implementation-defined behavior, and shall not exceed any minimum implementation limit.
¶6 The two forms of conforming implementation are hosted and freestanding. A conforming hosted implementation shall accept any strictly conforming program. A conforming freestanding implementation shall accept any strictly conforming program in which the use of the features specified in the library clause (clause 7) is confined to the contents of the standard headers
<float.h>,<iso646.h>,<limits.h>,<stdalign.h>,<stdarg.h>,<stdbool.h>,<stddef.h>,<stdint.h>, and<stdnoreturn.h>. A conforming implementation may have extensions (including additional library functions), provided they do not alter the behavior of any strictly conforming program.4)¶7 A conforming program is one that is acceptable to a conforming implementation.5)
3) A strictly conforming program can use conditional features (see 6.10.8.3) provided the use is guarded by an appropriate conditional inclusion preprocessing directive using the related macro. For example:
#ifdef __STDC_IEC_559__ /* FE_UPWARD defined */ /* ... */ fesetround(FE_UPWARD); /* ... */ #endif4) This implies that a conforming implementation reserves no identifiers other than those explicitly reserved in this International Standard.
5) Strictly conforming programs are intended to be maximally portable among conforming implementations. Conforming programs may depend upon non-portable features of a conforming implementation.
Примечательно, что единственный заголовок, необходимый для автономной среды, которая фактически определяет какие-либо функции, - это <stdarg.h> (и даже они могут быть - и часто являются - просто макросами).
Так же, как стандарт C распознает как размещенную, так и автономную среду, стандарт C++ тоже. (Цитаты из ISO / IEC 14882: 2011.)
1.4 Implementation compliance [intro.compliance]
¶7 Two kinds of implementations are defined: a hosted implementation and a freestanding implementation. For a hosted implementation, this International Standard defines the set of available libraries. A freestanding implementation is one in which execution may take place without the benefit of an operating system, and has an implementation-defined set of libraries that includes certain language-support libraries (17.6.1.3).
¶8 A conforming implementation may have extensions (including additional library functions), provided they do not alter the behavior of any well-formed program. Implementations are required to diagnose programs that use such extensions that are ill-formed according to this International Standard. Having done so, however, they can compile and execute such programs.
¶9 Each implementation shall include documentation that identifies all conditionally-supported constructs that it does not support and defines all locale-specific characteristics.3
3) This documentation also defines implementation-defined behavior; see 1.9.
17.6.1.3 Freestanding implementations [compliance]
Two kinds of implementations are defined: hosted and freestanding (1.4). For a hosted implementation, this International Standard describes the set of available headers.
A freestanding implementation has an implementation-defined set of headers. This set shall include at least the headers shown in Table 16.
The supplied version of the header
<cstdlib>shall declare at least the functionsabort,atexit,at_quick_exit,exit, andquick_exit(18.5). The other headers listed in this table shall meet the same requirements as for a hosted implementation.Table 16 — C++ headers for freestanding implementations
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 The
sizeofand_Alignofoperators…
¶8 EXAMPLE 3 In this example, the size of a variable length array is computed and returned from a function:
#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(), аргументы не будут проверяться. Для других функций это больше проблема - вам действительно нужен прототип в области видимости, когда функция вызывается, чтобы гарантировать правильность аргументов.
@DavidBowling: Обычно вы не вызываете main() рекурсивно за пределами таких мест, как IOCCC. У меня есть тестовая программа, которая это делает - в основном для новинок. Если у вас есть int i = 0; int main() { if (i++ < 10) main(i, i * i); return 0; }, и вы компилируете с GCC и не включаете -Wstrict-prototypes, он компилируется без ошибок при строгих предупреждениях. Если это main(void), он не компилируется.
Вот небольшая демонстрация использования кодов возврата ...
При использовании различных инструментов, которые предоставляет терминал Linux, можно использовать код возврата, например, для обработки ошибок после завершения процесса. Представьте, что присутствует следующий текстовый файл myfile:
This is some example in order to check how grep works.
Когда вы выполняете команду 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, чтобы предотвратить появление совпадений или обычных сообщений об ошибках. Однако это мелочи оболочки - ключевой момент, когда статус выхода может быть полезен - в порядке.
Обратите внимание, что стандарты C и C++ определяют два типа реализаций: автономные и размещенные.
Допустимые формы 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" для типа возвращаемого значения и параметров функции. Никакая другая форма не допускается.
Разрешена любая форма или название основного 2.
Допустимые формы 3:
int main (void)
int main (int argc, char *argv[])
/* or in some other implementation-defined manner. */
Комментарии:
C99 удалил "неявное int", поэтому main() больше не действителен.
Было введено странное двусмысленное предложение «или каким-либо другим способом, определяемым реализацией». Это можно интерпретировать как «параметры int main() могут отличаться» или как «main может иметь любую форму, определяемую реализацией».
Некоторые компиляторы предпочли интерпретировать стандарт именно так. Возможно, нельзя легко заявить, что они не соответствуют, цитируя стандарт сам по себе, поскольку он неоднозначен.
Однако, чтобы разрешить полностью дикие формы main(), вероятно (?) Не целью этого нового предложения. Обоснование C99 (не нормативное) подразумевает, что предложение относится к дополнительным параметрам int main4.
Тем не менее, в разделе о завершении программы в размещенной среде продолжаются споры о случае, когда main не возвращает int 5. Хотя этот раздел не является нормативным в отношении того, как следует объявлять main, он определенно подразумевает, что main может быть объявлен полностью определяемым реализацией способом даже в размещенных системах.
Разрешена любая форма или название основного 6.
Допустимые формы 7:
int main (void)
int main (int argc, char *argv[])
/* or in some other implementation-defined manner. */
Разрешена любая форма или название основного 8.
Обратите внимание, что int main() никогда не был указан как допустимая форма для любой размещенной реализации C ни в одной из вышеперечисленных версий. В C, в отличие от C++, () и (void) имеют разные значения. Первый - устаревшая функция, которую можно удалить из языка. См. Будущие языковые направления C11:
6.11.6 Function declarators
The use of function declarators with empty parentheses (not prototype-format parameter type declarators) is an obsolescent feature.
Допустимые формы 9:
int main ()
int main (int argc, char *argv[])
Комментарии:
Обратите внимание на пустую скобку в первой форме. В этом случае C++ и C отличаются, потому что в C++ это означает, что функция не принимает параметров. Но в C это означает, что он может принимать любой параметр.
Имя функции, вызываемой при запуске, определяется реализацией. Если он называется main(), он должен соответствовать заявленным формам 10:
// implementation-defined name, or
int main ()
int main (int argc, char *argv[])
Допустимые формы 11:
int main ()
int main (int argc, char *argv[])
Комментарии:
Текст стандарта был изменен, но он имеет то же значение.
Имя функции, вызываемой при запуске, определяется реализацией. Если он называется main(), он должен соответствовать заявленным формам 12:
// implementation-defined name, or
int main ()
int main (int argc, char *argv[])
использованная литература
The function called at program startup is named main. The implementation declares no prototype for this function. It shall be defined with a return type of int and with no parameters:
int main(void) { /* ... */ }
or with two parameters (referred to here as argc and argv, though any names may be used, as they are local to the function in which they are declared):
int main(int argc, char *argv[]) { /* ... */ }
In a freestanding environment (in which C program execution may take place without any benefit of an operating system), the name and type of the function called at program startup are implementation-defined.
The function called at program startup is named main. The implementation declares no prototype for this function. It shall be defined with a return type of int and with no parameters:
int main(void) { /* ... */ }
or with two parameters (referred to here as argc and argv, though any names may be used, as they are local to the function in which they are declared):
int main(int argc, char *argv[]) { /* ... */ }
or equivalent;9) or in some other implementation-defined manner.
The behavior of the arguments to main, and of the interaction of exit, main and atexit (see §7.20.4.2) has been codified to curb some unwanted variety in the representation of argv strings, and in the meaning of values returned by main.
The specification of argc and argv as arguments to main recognizes extensive prior practice. argv[argc] is required to be a null pointer to provide a redundant check for the end of the list, also on the basis of common practice.
main is the only function that may portably be declared either with zero or two arguments. (The number of other functions’ arguments must match exactly between invocation and definition.) This special case simply recognizes the widespread practice of leaving off the arguments to main when the program does not access the program argument strings. While many implementations support more than two arguments to main, such practice is neither blessed nor forbidden by the Standard; a program that defines main with three arguments is not strictly conforming (see §J.5.1.).
If the return type of the main function is a type compatible with int, a return from the initial call to the main function is equivalent to calling the exit function with the value returned by the main function as its argument;11) reaching the
}that terminates the main function returns a value of 0. If the return type is not compatible with int, the termination status returned to the host environment is unspecified.
In a freestanding environment (in which C program execution may take place without any benefit of an operating system), the name and type of the function called at program startup are implementation-defined.
Этот раздел идентичен упомянутому выше разделу C99.
Этот раздел идентичен упомянутому выше разделу C99.
An implementation shall not predefine the main function. This function shall not be overloaded. It shall have a return type of type int, but otherwise its type is implementation-defined. All implementations shall allow both of the following definitions of main:
int main() { /* ... */ }
and
int main(int argc, char* argv[]) { /* ... */ }
It is implementation-defined whether a program in a freestanding environment is required to define a main function.
An implementation shall not predefine the main function. This function shall not be overloaded. It shall have a return type of type int, but otherwise its type is implementation-defined. All implementations shall allow both
— a function of () returning int and
— a function of (int, pointer to pointer to char) returning int
as the type of main (8.3.5).
Этот раздел идентичен приведенному выше разделу 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 (), поскольку нет смысла возвращаться из программы в автономных системах.
Понятно. Но если разрешено использовать любое имя и любую подпись для функции запуска, почему бы не разрешить использовать другую подпись и для main? Извините, если это не умный вопрос, но я не мог понять причину.
@Utku C и C++ там разные. Что касается того, почему C++ обеспечивает это, я понятия не имею, нет никакого объяснения. Я подозреваю, что главный виновник (каламбур) - это Страуструп, который на раннем этапе заявил, что main должен возвращать int, период. Потому что, когда он сделал первую версию C++, он использовался только для хостинговых систем. В связанном сообщении Страуструп по-прежнему, кажется, не обращает внимания на существование автономных реализаций: например, он по незнанию ссылается на подглаву размещенной реализации стандарта C, игнорируя существование главы 5.1.2.1.
Примечательно, что черновик стандарта C11 состоит в том, что, хотя func() считается устаревшим, сам проект использует int main() в своих собственных примерах.
Что случилось с подписью с тремя аргументами int main(int argc, char **argv, char **envp);
@Spencer Это никогда не было стандартным. Он упоминается среди «общих расширений» в стандартном Приложении J.5.
return 0Когда программа C или C++ достигает конца main, компилятор автоматически генерирует код, возвращающий 0, поэтому нет необходимости явно помещать return 0; в конец main.
Примечание:, когда я делаю это предложение, за ним почти всегда следует один из двух видов комментариев: «Я этого не знал». или "Плохой совет!" Мое объяснение состоит в том, что безопасно и полезно полагаться на поведение компилятора, явно поддерживаемое стандартом. Для C, начиная с C99; см. ИСО / МЭК 9899: 1999 раздел 5.1.2.2.3:
[...] a return from the initial call to the
mainfunction is equivalent to calling theexitfunction with the value returned by themainfunction as its argument; reaching the}that terminates themainfunction returns a value of 0.
Для C++, начиная с первого стандарта в 1998 году; см. ИСО / МЭК 14882: 1998 раздел 3.6.1:
If control reaches the end of main without encountering a return statement, the effect is that of executing return 0;
С тех пор все версии обоих стандартов (C99 и C++ 98) сохранили ту же идею. Мы полагаемся на автоматически сгенерированные функции-члены в C++, и немногие люди пишут явные операторы return; в конце функции void. Причины отказа, похоже, сводятся к "это выглядит странно". Если вам, как и мне, интересно, в чем причина изменения стандарта C прочтите этот вопрос. Также обратите внимание, что в начале 1990-х это считалось «небрежной практикой», поскольку в то время это было неопределенное поведение (хотя и широко поддерживалось).
Кроме того, Основные принципы C++ содержит несколько экземпляров пропуска return 0; в конце main и никаких экземпляров, в которых записан явный возврат. Хотя в этом документе еще нет конкретного руководства по этой конкретной теме, это кажется, по крайней мере, молчаливым одобрением этой практики.
Так что я рекомендую опустить его; другие не согласны (часто категорически!). В любом случае, если вы встретите код, в котором он отсутствует, вы узнаете, что он явно поддерживается стандартом, и вы поймете, что это значит.
return 0;Это плохой совет, потому что компиляторы, которые реализуют только C89, а не какой-либо более поздний стандарт, очень распространены по-прежнему (я пишу это в 2017 году) и останутся чрезвычайно распространенными в обозримом будущем. Например, в последний раз я проверял версию нет компиляторов Microsoft, реализованных на C99, и, насколько я понимаю, это все еще типично для компиляторов встроенных систем, не являющихся GCC.
@zwol: у любого, у кого нет выбора, кроме как использовать компилятор, который устарел на 28 лет, вероятно, будет больше проблем, чем решить, включать ли явно return 0;, однако я бы отметил, что многие компиляторы той эпохи также реализовали неявный return 0; даже раньше. он был стандартизирован.
То, что вы говорите, правда. Я хочу только дать объяснение реакции «плохой совет», которая не просто «выглядит странно».
На самом деле, я много работаю со встроенными системами и более десяти лет не встречал компилятора, который не поддерживает неявный return 0. Также текущие версии Microsoft C также поддерживай это. Возможно, ваша информация устарела?
Я могу оценить это является спорным в C, почти (за @zwol). В C++ любые споры вокруг этого - полная чушь.
@LightnessRacesinOrbit Вы могли бы так поступить, но ... codereview.stackexchange.com/questions/221922/c-logging-libr ary /…
@ Эдвард Я не говорил, что спор не существует, я сказал, что это ерунда: P
What is the correct (most efficient) way to define the main() function in C and C++ — int main() or void main() — and why?
Эти слова «(наиболее эффективный)» не меняют вопроса. Если вы не находитесь в автономной среде, есть один универсально правильный способ объявить main(), и это как вернуть int.
What should
main()return in C and C++?
Дело не в том, что возвращает долженmain(), а в том, что возвращает делаетmain(). main() - это, конечно, функция, которую вызывает кто-то другой. У вас нет никакого контроля над кодом, который вызывает main(). Следовательно, вы должны объявить main() с правильным типом сигнатуры, чтобы соответствовать вызывающей стороне. У тебя просто нет выбора. Вам не нужно спрашивать себя, что более или менее эффективно, или какой стиль лучше или хуже, или что-то в этом роде, потому что ответ уже совершенно точно определен для вас стандартами C и C +. Просто следуй за ними.
If int main() then return 1 or return 0?
0 для успеха, ненулевое значение для неудачи. Опять же, это не то, что вам нужно (или нужно) выбирать: это определяется интерфейсом, которому вы должны соответствовать.
В C Раздел 5.1.2.2.1 стандарта C11 (выделено мной):
It shall be defined with a return type of
intand with no parameters:int main(void) { /* ... */ }or with two parameters (referred to here as
argcandargv, though any names may be used, as they are local to the function in which they are declared):int main(int argc, char *argv[]) { /* ... */ }
Однако для некоторых новичков, таких как я, абстрактный пример позволил бы мне понять это:
Когда вы пишете метод в своей программе, например int read_file(char filename[LEN]);, тогда вы, как вызывающий этот метод, хотите знать, все ли прошло хорошо (потому что могут произойти сбои, например, файл не может быть найден). Проверяя возвращаемое значение метода, вы можете узнать, все ли прошло хорошо или нет, это механизм для метода, который сигнализирует вам об успешном выполнении (или нет), и позволяет вызывающему (например, в вашем основном методе) решать как справиться с неожиданной ошибкой.
Итак, теперь представьте, что я пишу программу на языке C для микромеханизма, который используется в более сложной системе. Когда система вызывает микромеханизм, она хочет знать, все ли прошло, как ожидалось, чтобы она могла обработать любую потенциальную ошибку. Если основной метод программы C вернет void, то как вызывающая система узнает о выполнении своей подсистемы (микромеханизма)? Не может, поэтому main () возвращает int, чтобы сообщить вызывающей стороне успешное (или нет) выполнение.
Другими словами:
Рациональным является то, что среда хоста (то есть операционная система (ОС)) должна знать, правильно ли завершилась программа. Без int-совместимого типа в качестве возвращаемого типа (например, void) «статус, возвращаемый в среду хоста, не указан» (т.е. неопределенное поведение в большинстве ОС).
Я все еще думаю, что это довольно расплывчато. Определите для меня «самый эффективный». В каком смысле эффективен? В смысле занимать меньше памяти? В смысле быстрее бегать? Я вижу полезные ответы, но все же думаю, что вопрос сформулирован довольно плохо.