Требуется ли main () для программы на C? - PullRequest
42 голосов
/ 06 ноября 2010

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

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

Ответы [ 7 ]

63 голосов
/ 06 ноября 2010

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

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

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

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

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

9 голосов
/ 08 ноября 2010

Ну нет, но ...

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

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

Вот пример для Linux ...

$ 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.

9 голосов
/ 06 ноября 2010

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

3 голосов
/ 06 ноября 2010

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

  • Многие плагины для других программ (например, браузеры, текстовые редакторы и т. П.) Не имеют main().
  • Программы для Windows, написанные на C, не имеют main(). (Вместо них WinMain().)
0 голосов
/ 07 ноября 2010

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

0 голосов
/ 07 ноября 2010

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

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

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

0 голосов
/ 06 ноября 2010

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

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

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

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

...