Может кто-нибудь сказать, в чем проблема с кодом ниже?
int main () {
return main();
}
Я тестировал, он компилируется правильно. Он работает вечно. Неповторимый трюк за сценой?
Может кто-нибудь сказать, в чем проблема с кодом ниже?
int main () {
return main();
}
Я тестировал, он компилируется правильно. Он работает вечно. Неповторимый трюк за сценой?
TL;DR. Вызов main
приводит к поведению undefined.
Похоже, что существует путаница в терминологии, используемой в стандарте, и о последствиях для программиста и компилятора.
Во-первых, стандарт самостоятельно определяет все, что касается языка С++. Если ваша конкретная версия конкретного компилятора допускает какое-то конкретное действие, которое не имеет никакого отношения к тому, является ли это действие законным. Для остальной части сообщения я имею в виду стандарт ISO03.
Итак, чтобы еще раз процитировать, стандарт гласит в п. 3.3.1.3:
Функция main не должна использоваться внутри программы.
Кроме того, в §3.2 "используется" как:
Объект или неперегруженная функция используется, если его имя отображается в потенциально вычисленном выражении.
Это означает, что, как только программа начнет выполнение, main
должен никогда не вводиться снова. Это означает, что программисты не могут вызывать main
, это значит, что компилятор не может вставить другой вызов main
(почему он будет, кто знает), вы не можете взять адрес main и позвонить, и т.д. У вас даже не может быть потенциала вызов main
.
Единственный вызов main
должен выполняться библиотекой времени выполнения, в которой запущена программа; все остальные вызовы вызывают поведение undefined. (Это означает, что все может случиться!)
Теперь о поведении компилятора:
Диагностическое правило определяется как (§1.4.1):
Набор диагностируемых правил состоит из всех синтаксических и семантических правил в этом Международном стандарте, за исключением тех правил, которые содержат явное обозначение, что "никакой диагностики не требуется" или которые описаны как результат "undefined поведения".
В нашем случае в п. 3.3.1.3 определяется правило диагностики. Вот какие компиляторы должны делать в соответствии с §1.4.2:
- Если программа не содержит нарушений правил в этом Международном стандарте, соответствующая реализация должна в пределах своих ресурсов принимать и правильно выполнять3) эту программу.
- Если программа содержит нарушение любого диагностируемого правила, соответствующая реализация должна выпустить хотя бы одно диагностическое сообщение, за исключением того, что - Если программа содержит нарушение правила, для которого не требуется диагностика, настоящий международный стандарт не устанавливает требований к реализации в отношении этой программы.
Поэтому компиляторы не обязаны применять правила. Все компиляторы должны сделать, это взять хорошо сформированные программы (§1.3.14) и превратить их в исполняемую программу. Компилятор может свободно предупреждать, ошибочно и т.д., Однако ему нравится, если он не конфликтует с языком. В соответствии со вторым предложением необходимо отобразить сообщение в нашем конкретном случае.
Для этой конкретной задачи на gcc параметр -pedantic
будет предупреждать о незаконности вызова main
внутри программы. Visual Studio не будет предупреждать о вызове main
, но на любом уровне предупреждения (больше 0) он будет предупреждать о рекурсивном характере программы.
Что все это означает с точки зрения ответов, которые вы должны ожидать? Это означает, что совершенно бессмысленно пытаться определить с уверенностью то, что опубликует фрагмент кода. Вызов main
приводит к поведению undefined, и попытка определения поведения undefined, очевидно, является потерянной причиной. Единственный честный ответ, который любой может дать "что происходит, когда я называю main
?"? "Что угодно".
Надеюсь, это прояснит ситуацию.
Вызов main
в С++ является незаконным (§3.6.1.3):
Функция main не должна использоваться внутри программы.
Ваш компилятор разрешает незаконное поведение.
Он циклически навсегда, потому что, main
вызывает main
, который вызывает main
, который вызывает main
и т.д.
Ему нравится быть наркодилером. Довольно незаконно, но компилируется и даже работает хорошо в течение некоторого времени...
Вопрос в том, зачем вы хотите?
main должна быть единственной точкой входа для вашей программы. Вызов его снова существенно перезапустит вашу программу, но без нового экземпляра процесса; нет нового стека, нет новой кучи и т.д.
Если вам действительно нужна рекурсия, вызовите отдельную функцию рекурсивно.
В стандарте С++ в разделе 3.6.1 говорится:
Функция main не должна использоваться (3.2) внутри программы.
Он указывает, что вы не должны вызывать его из вашей программы.
Конечно, если вы действительно хотите рекурсивно вызывать свою основную функцию, и иногда есть веские причины, вы должны просто сделать это
int mymain()
{
return mymain();
}
int main()
{
return mymain();
}
Когда вы пишете рекурсивный код, вам нужно убедиться, что в какой-то момент вы перестанете рекурсировать, иначе вы просто написали бесконечный цикл.
Что у вас есть.
Вы должны ожидать, что этот код уйдет в течение длительного времени, а затем, наконец, сбой при переполнении стека.
У вас есть два вопроса. Первый - это вызов main, который, как уже указывалось, нарушает стандарт и цель стандарта.
Большая проблема заключается в том, что вы написали рекурсивный вызов без какой-либо точки закрытия. Вы сомневаетесь, что предположите, что версия main, вызванная исходной, просто вернется. Однако большинство языков (на самом деле все, о чем я могу думать) допускает неограниченную рекурсию: если функция вызывает себя, то эта версия будет также. Единственным ограничением является системные ресурсы.
Итак, вам нужно обернуть вызов в условном выражении и продолжать звонить только при необходимости. В вашем примере будет добавлено глобальное целое число, установленное на количество уровней, которые вы хотите обработать. Что-то вроде этого:
`
int levels = 3;
int main() {
if(levels) {
cout << "Recursing another level...\n";
levels--;
main();
}
else {
cout << "Reached the bottom.\n";
}
return levels;
}
`
Выйдет.
Несмотря на то, что ваша программа явно не имеет смысла, поскольку она навсегда навсегда, возможно, будет возможно сделать что-то вроде:
int main( int argc, char* argv[] )
{
if( argc )
{
// do something with argv[0]
main( argc - 1, &argv[1] );
}
else
{
// rest of application having loaded your arguments
}
}
Однако стандарт запрещает его. (См. Другие ответы)
Конечно, вы можете реализовать это, сделав это
int myFunc( int argc, char * argv[] )
{
// I can recurse this if I like and just get main to forward straight to here
}