Первая программа на C и этапы компиляции

⏱ 20 мин · 🧩 заданий: 5

Почему C

Операционные системы — Linux, FreeBSD, ядро Windows — в основном написаны на C. Системные вызовы, которые мы будем изучать (fork, mmap, kill, open), описаны как функции C. Экзаменационные задачи тоже приходят в виде кода на C. Поэтому начнём с языка.

C — компилируемый язык низкого уровня: программа переводится прямо в машинный код процессора, а программист сам управляет памятью через указатели.

Первая программа

#include <stdio.h>          /* подключаем объявления функций ввода-вывода */

int main(void)              /* отсюда начинается выполнение программы */
{
    printf("Hello, OS!\n"); /* печать строки; \n — перевод строки */
    return 0;               /* код завершения: 0 — успех */
}

Разберём построчно:

Строка Что значит
#include <stdio.h> Директива препроцессора: вставить сюда содержимое заголовка stdio.h, где объявлена printf
int main(void) Главная функция. int — она возвращает целое число, void — не принимает аргументов
{ ... } Тело функции
printf(...) Вызов функции печати из стандартной библиотеки
return 0; Завершение main с кодом 0

Каждая инструкция заканчивается точкой с запятой ;. Комментарии пишутся как /* ... */ или // ....

Код завершения

Число из return в main становится кодом завершения процесса (exit status). По соглашению 0 — успех, всё остальное — ошибка. Родительский процесс получает этот код через системный вызов wait — подробно разберём в модуле о процессах.

$ ./hello
Hello, OS!
$ echo $?
0

Аналогия

Код завершения — как отметка в журнале: процесс уходит и оставляет родителю короткий итог «всё хорошо» (0) или номер проблемы.

Четыре этапа сборки

Команда gcc hello.c -o hello на самом деле запускает цепочку из четырёх программ:

hello.c ──[препроцессор cpp]──▶ hello.i ──[компилятор cc1]──▶ hello.s │ hello ◀──[компоновщик ld]──── hello.o ◀──[ассемблер as]─────────┘ ▲ └── + библиотеки (libc.so: printf, fork, ...)
  1. Препроцессор обрабатывает строки с #: вставляет файлы #include, подставляет макросы #define, выкидывает комментарии. Результат — чистый текст на C.
  2. Компилятор переводит C в язык ассемблера конкретного процессора.
  3. Ассемблер превращает ассемблер в машинный код — объектный файл .o. В нём ещё есть «дыры»: например, адрес printf неизвестен.
  4. Компоновщик (линкер) соединяет объектные файлы и библиотеки, заполняет адреса функций и создаёт исполняемый файл.

Каждый этап можно увидеть отдельно:

gcc -E hello.c -o hello.i   # только препроцессор
gcc -S hello.c -o hello.s   # до ассемблера
gcc -c hello.c -o hello.o   # до объектного файла
gcc hello.o -o hello        # компоновка
Что на самом деле вызывает printf?

printf — функция библиотеки libc. Она форматирует строку в буфер в памяти процесса, а когда буфер заполнится (или встретится \n при выводе в терминал), вызывает системный вызов write(1, буфер, длина). Только write обращается к ядру — оно и выводит символы на терминал. Буферизацию мы ещё вспомним в задаче про fork.

Полезные флаги gcc

Флаг Назначение
-o имя Имя выходного файла
-Wall -Wextra Включить предупреждения — всегда используйте
-g Отладочная информация для gdb
-O2 Оптимизация
-std=c11 Стандарт языка
-lm, -lpthread Подключить библиотеку (libm, libpthread)

Привычка

Компилируйте с -Wall -Wextra: компилятор найдёт половину ошибок до запуска программы.

Итог

  • C — компилируемый язык, на котором пишут ядра и системные программы.
  • Выполнение начинается с main; её return — код завершения процесса.
  • Сборка: препроцессор → компилятор → ассемблер → компоновщик.
  • printf — библиотечная функция, а работу с устройством выполняет системный вызов write.

Практика решено 0 из 5

Вы не вошли — ответы проверяются, но прогресс не сохранится. Войти или зарегистрироваться.
Задание 1 Расставить по порядку

Расставьте этапы превращения hello.c в исполняемый файл в правильном порядке:

  1. Ассемблер: переводит ассемблер в машинный код → объектный файл hello.o
  2. Препроцессор: подставляет #include, раскрывает #define → hello.i
  3. Компоновщик (линкер): соединяет hello.o с библиотеками → исполняемый файл hello
  4. Компилятор: переводит C в ассемблер → hello.s
Задание 2 Написать код

Напишите программу, которая печатает строку Hello, OS! и перевод строки, а затем возвращает операционной системе код успешного завершения 0.

Задание 3 Один вариант ответа

Какой флаг gcc остановит сборку после компиляции и ассемблирования, получив только объектный файл .o?

Задание 4 Ввод ответа

Программа завершилась return 3; из main. Какое число выведет команда echo $?, выполненная сразу после неё в shell?

Задание 5 Несколько вариантов

Почему ядра ОС (Linux, BSD) и системные утилиты пишут на C?

Войдите, чтобы отмечать пройденные уроки.