Требуется ли main() для программы на C?

Ну название говорит само за себя. Является ли функция main() абсолютно необходимой для программы на C?

Я спрашиваю об этом, потому что просматривал код ядра Linux и не видел функции main().


person elricL    schedule 06.11.2010    source источник


Ответы (7)


Нет, в стандарте ISO C указано, что функция main требуется только для размещенной среды (например, с базовой ОС).

Для автономной среды, такой как встроенная система (или сама операционная система), это определяется реализацией. Из C99 5.1.2:

Определены две среды выполнения: автономная и размещенная. В обоих случаях запуск программы происходит, когда назначенная функция C вызывается средой выполнения.

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

Что касается запуска самого Linux, отправной точкой для ядра Linux является start_kernel, однако для получения более полной картины всего процесса загрузки следует запустить здесь.

person paxdiablo    schedule 06.11.2010
comment
Там, где хочет разработчик. - person R.. GitHub STOP HELPING ICE; 06.11.2010
comment
@Mad-scientist, см. обновление start_kernel в init/main.c. - person paxdiablo; 06.11.2010
comment
На самом деле, я понимаю ответ, прочитав его снова. Если это какая-то встроенная система, не будет какой-либо фиксированной начальной точки, ее можно запустить с помощью переключателей или сбросов. Спасибо. - person elricL; 06.11.2010
comment
Даже если бы стандарт C ничего не говорил о независимых реализациях и настаивал на том, что каждая программа должна иметь main, ядро ​​Linux все равно не требовало бы его, потому что, черт возьми, это не соответствующая программа. Это означает, что у вас нет уверенности в том, что вы сможете скомпилировать его на своем компьютере с Windows с помощью MSVC и заставить его работать правильно — но ведь вы и не ожидали, что сможете, не так ли? Большинство людей здесь не понимают Стандарт — он дает определенные гарантии, если вы делаете определенные вещи, но не может помешать тому, чтобы что-то работало, если вы этого не делаете. - person Jim Balter; 21.02.2011
comment
@Mad-scientist Нет, ты не понял. main не является фиксированной начальной точкой, это произвольное место в программе, которое записывается компоновщиком в исполняемый файл. Но можно выбрать и другие имена, и другое для Linux. И стандарт C здесь неуместен, потому что ядро ​​Linux не является соответствующей программой и не скомпилировано соответствующей реализацией — уж точно не с теми флагами, которые используются для сборки ядра Linux. Некоторые дураки говорят, но тогда может взорваться термоядерная бомба — они не понимают, как работает Стандарт. - person Jim Balter; 21.02.2011
comment
@agf, хм, китайский сайт, кажется, пошел на попятную. Заменил их некоторыми страницами TLDP. - person paxdiablo; 14.10.2011

Ну нет, но...

C99 указывает, что main() вызывается в размещенной среде «при запуске программы», однако вам не нужно использовать поддержку среды выполнения C. Ваша операционная система выполняет файлы изображений и запускает программу по адресу, предоставленному компоновщиком.

Если вы хотите написать свою программу, соответствующую требованиям операционной системы, а не требованиям C99, вы можете сделать это без main(). Однако чем более современная (и сложная) система, тем больше проблем у вас будет с библиотекой C, делающей предположения, что используется стандартный запуск во время выполнения.

Вот пример для линукса...

$ cat > nomain.S
.text
_start:
    call    iamnotmain
    movl    $0xfc, %eax
    xorl    %ebx, %ebx
    int     $0x80
.globl _start
$ cat > demo.c

void iamnotmain(void) {
    static char s[] = "hello, world\n";
    write(1, s, sizeof s);
}
$ as -o nomain.o nomain.S
$ cc -c demo.c
$ ld -static nomain.o demo.o -lc
$ ./a.out
hello, world

Однако теперь это, возможно, не «программа C99», а просто «программа Linux» с объектным модулем, написанным на C.

person DigitalRoss    schedule 08.11.2010
comment
+1. Я бы по-прежнему считал это программой C99, просто автономной. Ничто в стандарте не говорит о том, что вы должны использовать ОС только потому, что она есть :-) - person paxdiablo; 08.11.2010

Функция main() вызывается объектным файлом, включенным в libc. Поскольку ядро ​​не связывается с libc, у него есть собственная точка входа, написанная на ассемблере.

person Ignacio Vazquez-Abrams    schedule 06.11.2010

ответ Paxdiablo охватывает два случая, когда вы выиграли не встречал главного. Добавлю еще парочку:

  • Многие системы подключаемых модулей для других программ (например, браузеров, текстовых редакторов и т.п.) не имеют main().
  • Программы Windows, написанные на C, не имеют main(). (Вместо этого у них WinMain().)
person JUST MY correct OPINION    schedule 06.11.2010
comment
В программах для Windows есть WinMain, потому что функция main() находится в заглушке, которая делает некоторые вещи, а затем вызывает WinMain. - person Kinjal Dixit; 06.11.2010
comment
Так же, как программы GCC, скажем, имеют main(), потому что _start() (я думаю) находится в шаблонном коде в libc, который делает некоторые вещи, а затем вызывает main(). Я не уверен, что вы имеете в виду здесь. Или вы думали, что main() вызывается непосредственно операционной системой? - person JUST MY correct OPINION; 07.11.2010

Загрузчик операционной системы должен вызывать единую точку входа; в компиляторе GNU точка входа определяется в связанном объектном файле crt0.o, источником для которого является файл ассемблера crt0.s, который вызывает main() после выполнения различных задач запуска во время выполнения (таких как установка стек, статическая инициализация). Поэтому при создании исполняемого файла, который связывает crt0.o по умолчанию, вы должны иметь main(), иначе вы получите ошибку компоновщика, поскольку в crt0.o main() является неразрешенным символом.

Можно было бы (хотя и несколько извращенно и необязательно) модифицировать crt0.s для вызова другой точки входа. Просто убедитесь, что вы делаете такой объектный файл специфичным для вашего проекта, а не изменяете версию по умолчанию, иначе вы сломаете каждую сборку на этой машине.

Сама ОС имеет свой собственный запуск среды выполнения C (который будет вызываться из загрузчика), поэтому может вызывать любую точку входа, которую пожелает. Я не смотрел исходный код Linux, но представьте, что у него есть собственный crt0.s, который будет вызывать любую точку входа кода C.

person Clifford    schedule 06.11.2010
comment
Загрузчик операционной системы не вызывает main(). - person JUST MY correct OPINION; 07.11.2010

main вызывается glibc, который является частью приложения (кольцо 3), а не ядра (кольцо 0).
у драйвера есть другая точка входа, например, база драйвера Windows на WDM запускается из DRIVERENTRY

person Sim Sun    schedule 07.11.2010

В машинном языке все выполняется последовательно, то, что приходит первым, выполняется первым. Таким образом, по умолчанию компилятор вызывает основной метод, соответствующий стандарту C.

Ваша программа работает как библиотека, представляющая собой набор скомпилированных функций. Основное различие между библиотекой и стандартным исполняемым файлом заключается в том, что для второго компилятор генерирует ассемблерный код, который вызывает одну из функций вашей программы.

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

Это было задумано как общее и поверхностное объяснение, есть некоторые технические различия, которые я намеренно избегал, поскольку они не кажутся уместными.

person Trinidad    schedule 06.11.2010