Тип, возвращаемый функцией main

237
02 июля 2022, 22:30

Во многих примерах кода на сайте встречаются как записи вида void main(), так и int main() для главной функции программы.

Какой из них правильный и в чём вообще разница?

Answer 1

Стандарт языка требует обязательного наличия двух возможных реализаций:

  • int main()
    
  • int main(int, char**)
    

То есть в обоих случаях возвращаемым типом является int. Однако, разрешается существование и дополнительных вариантов в том или ином компиляторе:

Its type shall have C++ language linkage and it shall have a declared return type of type int, but otherwise its type is implementation-defined.

Таким образом, вариант, возвращающий int является переносимым между разными реализациями компиляторов, а void к таковым не относится. Но в частности void main() поддерживается компилятором Microsoft Visual C++.

Возвращаемое значение main в общем случае говорит об успешности работы программы. Если всё прошло хорошо, то возвращается ноль - return 0;; иначе следует вернуть какое-то другое целочисленное значение, которое можно обработать на стороне, вызвавшей программу. Разрешается явно не писать return 0; в main. В таком случае, при выходе из функции 0 будет подразумеваться. Аналогичное поведение распространяется и на void вариант от MS. Но если в случае с int код возврата можно вернуть явно через return, то с void main() такой вариант уже не пройдет:

error C2562: 'main': 'void' function returning a value

Правда, вместо этого можно использовать функцию exit(0), которая прерывает выполнение программы и возвращает переданный код вызывающей стороне.

Answer 2

Данная проблема является проблемой линковщика, а не компилятора.

Нужно понимать, что main является _cdecl функцией, т.е. функции всёравно сколько параметров, потому что, опорная библиотека будет иметь примерно такой вид

push ecx; argv
push eax; argc
call _main
pop ecx
pop ecx
push eax ; Код возрата
call _exit ; И на выход с кодом возврата

Т.е. фактически если ф-ция будет int main() - стек не испортится, просто функция не увидит argv и argc. А функция void main(int argc, int argv) получит оба параметра.

Так как опора идет на сигнатуру _main, то на эту сигнатуру можно посадить любую функцию, которая или не имеет параметров, или имеет метку _cdecl (если это не запретит компилятор). Для получения такой сигнатуры нужно указать что используются страрые сигнатуры, делается это указанием extern "C" (это указание присутствует в стандартных библиотеках, благодаря которым main и срабатывает). Т.е. могут прокатить виды extern "C" int _cdecl main(...); int _cdecl main(...){} а так же аналогично void main(), int main(int argc) и даже long _cdecl main(...);, int _cdecl main(int argc, char** argv, int secparam1, int secparam2, int secparam3)

Теперь о возвращаемом типе. Опорная библиотека ожидает, что в eax прийдет результат. При использовании void - программа может вернуть "мусор", который вернётся из eax, с этой точки зрения int main() лучше. Но последнее время, код возврата программы нигде не анализируется, поэтому если ф-ция вернула "мусор" - это с большой вероятностью ни на что не повлияет.

Если вы пишите под батник, или под програмный комплекс (инсталятор например) , где проверяется код возврата - то нужно писать int main() и возвращать нужные значения.

Так же, есть функции exit terminate, и есть обработчики исключений, которые прерыват программу в обход main - если программа прерывается всегда "насильно" - то возвращаемый тип перестаёт играть свою роль.

READ ALSO
Getter и Setter для указателя

Getter и Setter для указателя

Вопрос заключается в том, как правильно реализовать Getter для указателя, чтобы он был "безопасным"Под безопасностью я подразумеваю предотвращение...

262
Как использовать std::bind с методами класса?

Как использовать std::bind с методами класса?

Пытаюсь использовать std::bind с методом класса:

221
Преобразование типа вектора из uint8 в uint16

Преобразование типа вектора из uint8 в uint16

Есть вектор std::vector<uint8_t> From; Он содержит 10^9 элементов

208