Я знаю, что есть две разные подписи для написания основного метода -
int main()
{
//Code
}
или для обработки аргумента командной строки мы пишем его как-
int main(int argc, char * argv[])
{
//code
}
В C++
Я знаю , что мы можем перегрузить метод, но C
как компилятор обрабатывать эти две различные сигнатуры main
функции?
main
метод в одной программеC
(или, на самом деле, практически на любом языке с такой конструкцией).main
- я рекомендую классическую книгу Джона Р. Левинеса "Linkers & Loaders".int main(void)
notint main()
(хотя я никогда не видел компилятора, который отклоняетint main()
форму).()
форма устарела, и неясно, разрешено ли она вообщеmain
(если реализация специально не документирует ее как разрешенную форму). В стандарте C (см. 5.1.2.2.1 Запуск программы)()
форма не упоминается , что не совсем эквивалентно()
форме. Детали слишком длинные для этого комментария.Ответы:
Некоторые особенности языка C начинались как хитрости, которые просто так сработали.
Одной из таких функций является наличие нескольких подписей для основных списков аргументов и списков аргументов переменной длины.
Программисты заметили, что они могут передавать функции дополнительные аргументы, и с их компилятором ничего плохого не происходит.
Это так, если соглашения о вызовах таковы, что:
Один набор соглашений о вызовах, который подчиняется этим правилам, - это передача параметров на основе стека, при которой вызывающий объект выталкивает аргументы, и они перемещаются справа налево:
В компиляторах, где используется этот тип соглашения о вызовах, ничего особенного не нужно делать для поддержки двух типов
main
или даже дополнительных типов.main
может быть функцией без аргументов, и в этом случае она не обращает внимания на элементы, которые были помещены в стек. Если это функция двух аргументов, то он находитargc
иargv
как два самых верхних элемента стека. Если это зависящий от платформы вариант с тремя аргументами и указателем среды (обычное расширение), это тоже будет работать: он найдет этот третий аргумент как третий элемент сверху стека.Таким образом, фиксированный вызов работает во всех случаях, позволяя связать один фиксированный модуль запуска с программой. Этот модуль можно было бы написать на C как функцию, похожую на эту:
Другими словами, этот стартовый модуль всегда вызывает основную функцию с тремя аргументами. Если main не принимает аргументов или принимает только аргументы,
int, char **
он работает нормально, а также, если он не принимает аргументов, из-за соглашений о вызовах.Если бы вам пришлось делать такие вещи в своей программе, это было бы непереносимым и считалось бы неопределенным поведением ISO C: объявление и вызов функции одним способом и определение ее другим. Но трюк с запуском компилятора не обязательно должен быть переносимым; он не руководствуется правилами переносимых программ.
Но предположим, что соглашения о вызовах таковы, что это не может работать таким образом. В таком случае компилятор должен относиться к нему
main
специально. Когда он замечает, что компилируетmain
функцию, он может сгенерировать код, совместимый, скажем, с вызовом с тремя аргументами.То есть вы пишете это:
Но когда компилятор видит это, он по существу выполняет преобразование кода, так что функция, которую он компилирует, выглядит примерно так:
за исключением того, что имена
__argc_ignore
буквально не существуют. Такие имена не вводятся в вашу область видимости, и не будет никаких предупреждений о неиспользуемых аргументах. Преобразование кода заставляет компилятор генерировать код с правильной связью, который знает, что он должен очистить три аргумента.Другая стратегия реализации заключается в том, что компилятор или, возможно, компоновщик может самостоятельно сгенерировать
__start
функцию (или как бы она ни называлась) или, по крайней мере, выбрать одну из нескольких предварительно скомпилированных альтернатив. В объектном файле может храниться информация о том, какая из поддерживаемых формmain
используется. Компоновщик может просмотреть эту информацию и выбрать правильную версию модуля запуска, который содержит вызовmain
, совместимый с определением программы. Реализации C обычно имеют лишь небольшое количество поддерживаемых форм,main
поэтому такой подход осуществим.Компиляторы для языка C99 всегда должны обрабатывать
main
до некоторой степени специально, чтобы поддержать хак, что если функция завершается безreturn
оператора, поведение будет таким, как если бы онаreturn 0
была выполнена. Это, опять же, можно лечить преобразованием кода. Компилятор замечает, что вызываемая функцияmain
компилируется. Затем он проверяет, доступен ли конец тела. Если это так, он вставляетreturn 0;
источник
Нет НИКАКОЙ перегрузки
main
даже в C ++. Основная функция - это точка входа в программу, и должно существовать только одно определение.Для стандарта C
Для стандартного C ++:
Стандарт C ++ явно говорит: «Она [основная функция] должна иметь возвращаемый тип типа int, но в остальном ее тип определяется реализацией» и требует тех же двух сигнатур, что и стандарт C.
В размещенной среде ( среда AC, которая также поддерживает библиотеки C) - вызывает операционная система
main
.В среде без хоста (предназначенной для встраиваемых приложений) вы всегда можете изменить точку входа (или выхода) своей программы, используя директивы препроцессора, такие как
Где приоритет - это необязательное целое число.
Запуск Pragma выполняет функцию перед основной (по приоритету), а программа exit выполняет функцию после основной функции. Если имеется более одной директивы запуска, то приоритет решает, какая из них будет выполнена первой.
источник
Нет необходимости в перегрузке. Да, существует 2 версии, но одновременно можно использовать только одну.
источник
Это одна из странных асимметрий и особых правил языков C и C ++.
На мой взгляд, он существует только по историческим причинам и в этом нет никакой серьезной логики. Обратите внимание, что это
main
является особенным и по другим причинам (например,main
в C ++ не может быть рекурсивного, и вы не можете взять его адрес, а в C99 / C ++ вам разрешено опустить последнийreturn
оператор).Заметьте также, что даже в C ++ это не перегрузка ... либо программа имеет первую форму, либо вторую форму; он не может иметь обоих.
источник
return
оператор в C (начиная с C99).main()
и взять его адрес; C ++ применяет ограничения, которых нет в C.argc
при рекурсии (5.1.2.2.1 не указывает ограничений наargc
иargv
применяется только к первоначальному вызовуmain
).Необычность
main
заключается не в том, что его можно определить более чем одним способом, а в том, что его можно определить только одним из двух разных способов.main
это функция, определяемая пользователем; реализация не объявляет для него прототип.То же самое верно и для
foo
илиbar
, но вы можете определять функции с этими именами как хотите.Разница в том, что
main
вызывается реализацией (средой выполнения), а не только вашим собственным кодом. Реализация не ограничивается обычной семантикой вызова функций C, поэтому она может (и должна) иметь дело с несколькими вариациями, но не требуется обрабатывать бесконечно много возможностей.int main(int argc, char *argv[])
Форма позволяет аргументы командной строки, иint main(void)
в C илиint main()
в C ++ это просто удобство для простых программ , которые не нужны для аргументов командной строки процесса.Что касается того, как компилятор это обрабатывает, это зависит от реализации. Большинство систем, вероятно, имеют соглашения о вызовах, которые обеспечивают эффективную совместимость двух форм, и любые аргументы, переданные в объект,
main
определенный без параметров, незаметно игнорируются. В противном случае компилятору или компоновщику не составит труда обработать этоmain
специально. Если вам интересно, как это работает в вашей системе , вы можете посмотреть некоторые списки сборок.И, как и многие вещи в C и C ++, детали во многом являются результатом истории и произвольных решений, принятых разработчиками языков и их предшественниками.
Обратите внимание, что и C, и C ++ допускают другие определения, определяемые реализацией,
main
но редко есть веские причины для их использования. А для автономных реализаций (таких как встроенные системы без ОС) точка входа в программу определяется реализацией и даже не вызываетсяmain
.источник
Это
main
просто имя для начального адреса, определяемого компоновщиком, гдеmain
имя по умолчанию. Все имена функций в программе - это начальные адреса, с которых функция запускается.Аргументы функции выталкиваются / выталкиваются в / из стека, поэтому, если для функции не указаны аргументы, нет никаких аргументов, выталкиваемых / выталкиваемых в / из стека. Таким образом main может работать как с аргументами, так и без них.
источник
где переменная argc хранит количество переданных данных, а argv - это массив указателей на char, который указывает на переданные значения из консоли. В противном случае всегда хорошо идти с
Однако в любом случае в программе может быть один и только один main (), поскольку это единственная точка, с которой программа начинает свое выполнение, и, следовательно, их не может быть больше одного. (надеюсь, что это достойно)
источник
Подобный вопрос задавался раньше: почему функция без параметров (по сравнению с фактическим определением функции) компилируется?
Один из самых популярных ответов был:
Итак, я предполагаю, что это то, как
main
объявляется (если вы можете применить термин «объявленный»main
). На самом деле вы можете написать примерно так:и он все равно будет компилироваться и запускаться.
источник
main
, поскольку есть еще не упомянутая проблема: еще больше аргументов в пользуmain
! Добавляют «Unix (но не Posix.1) и Microsoft Windows»char **envp
(я помню, что DOS тоже позволяла это, не так ли?), А Mac OS X и Darwin добавляют еще один указатель char * на «произвольную информацию, предоставляемую ОС». википедияВам не нужно переопределять это. Потому что одновременно будет использоваться только одна. Да, есть 2 разные версии основной функции
источник