Мыслим на Си
Введение
Знаете, что общего у огненной птицы Phoenix и языка программирования Си? Оба возрождаются из пепла старого мира, чтобы создать новый.
Когда мне было шесть лет, я впервые увидела надпись на экране: "Phoenix BIOS 4.0 Release 6.0". Папа сказал, что это птица, которая живёт в компьютере и помогает ему проснуться. Тогда я ещё не знала, что настоящая магия начинается дальше – когда на экране появляется чёрное окно терминала, и компьютер спрашивает: "Что ты хочешь мне сказать?"
Этот учебник – о языке, на котором я научилась думать. О языке Си. Не о синтаксисе (хотя и о нём тоже). А о том, как мыслить на языке машины так же естественно, как мы мыслим на русском или английском.
История: откуда взялся Си и почему он такой
В 1969 году Деннис Ритчи и Кен Томпсон работали в Bell Labs над операционной системой, которую назвали UNIX. Им нужен был язык – не слишком высокоуровневый, как COBOL, но и не ассемблер, где каждая строчка – это боль. Сначала Томпсон создал язык B (да, просто буква B), но он был слишком простым.
Тогда Ритчи взял B и добавил типы данных, структуры, указатели – всё то, что позволяет работать с памятью напрямую, но при этом оставаться человеком. Так родился Си – язык, который стал языком самой UNIX. Представьте: операционная система написана на языке, который был создан специально для неё. Змея, кусающая свой хвост. Красиво, правда?
Потом случилось чудо. В 1983 году Ричард Столлман, программист с бородой и идеями свободы, запустил проект GNU – "GNU's Not Unix". Он хотел создать свободную операционную систему, где каждый мог бы видеть исходный код, менять его, учиться на нём. И весь этот проект был написан на Си.
А в 1991 году финский студент Линус Торвальдс сидел в своей комнате в Хельсинки и думал: "А что если я напишу своё ядро? Просто так, для хобби". Он написал первую версию Linux – тоже на Си. И выложил её в интернет с простым сообщением: "Я делаю свободную операционную систему. Кто хочет помочь?"
Сегодня Linux работает на миллиардах устройств – от серверов Google до вашего Android-смартфона. И весь этот код – открытый, свободный, написанный на Си. Вы можете зайти на GitHub прямо сейчас, открыть исходники ядра Linux и увидеть, как работает операционная система изнутри. Никаких секретов. Никакой магии. Только логика и код.
Философия Unix: всё – это файл
Unix придумали философию, которая кажется безумной, пока вы её не поймёте: всё есть файл. Ваша клавиатура? Файл (/dev/input). Ваш монитор? Файл (/dev/fb0). Даже процессы, которые сейчас выполняются на вашем компьютере – это файлы в директории /proc.
Память – это тоже файл. Вы можете открыть /dev/mem и прочитать содержимое оперативной памяти как обычный текстовый документ. Процессор ничем не отличается от жёсткого диска. Всё прозрачно. Всё доступно.
Почему это важно? Потому что когда всё – файл, то всё работает одинаково. Вы открываете файл функцией open(), читаете его функцией read(), закрываете функцией close(). Неважно, что это: текстовый документ, видеокарта или сетевое соединение. Один интерфейс. Одна логика.
Это как если бы в реальном мире вы могли открыть дверь, книгу, банку с вареньем и человеческое сердце – одним и тем же ключом. Звучит странно? Но именно так работает Unix. И именно поэтому Си так идеально подходит для этой системы.
Почему Си – это не просто язык программирования
Когда соседская девочка Настя учила английский, она повторяла за учительницей: "The cat is on the table". Она не понимала грамматику. Она не знала, что такое артикль или время глагола. Но она чувствовала структуру языка.
Я учила Си так же. Повторяла за компьютером: printf(), return, #include. Не понимала, почему нужны фигурные скобки или точка с запятой. Но я чувствовала ритм языка. Логику. Красоту структуры.
Си – это не набор правил. Это способ думать. Когда вы пишете на Си, вы думаете на языке памяти, указателей, процессора. Вы не говорите компьютеру "сделай что-то". Вы объясняете ему как это сделать – шаг за шагом, байт за байтом.
На англоязычных форумах вроде моего любимого Reddit есть легенда: если вы понимаете указатели в Си, вы понимаете 80% всего программирования. Потому что указатели – это суть того, как работает память. А память – это суть того, как работает компьютер.
Для кого эта книга?
Для тех, кто хочет понять, как работает настоящий компьютер. Не интерфейсы и кнопочки, а железо. Память. Процессор. Файловая система.
Может быть, вам семь лет, и вы только научились читать. Может быть, вам семьдесят, и вы решили изучить программирование. Неважно. Если вы можете думать логически – вы можете изучить Си.
Я не буду врать: это будет сложно. Си не прощает ошибок. Он не держит вас за руку. Он говорит: "Вот память. Вот процессор. Вот компилятор. Дальше – сам".
Но знаете что? Именно поэтому он честный. Прозрачный. Правильный.
Структура книги
Мы начнём с самого начала – с первой программы "Hello, World". Потом изучим типы данных, операторы, циклы. Затем нырнём глубже: функции, указатели, работа с памятью. И в самом конце – файлы, процессы, многопоточность.
Каждая глава – это новый уровень понимания. Новый слой абстракции, который мы снимаем, чтобы увидеть, что находится внутри.
К концу этой книги вы сможете прочитать исходный код ядра Linux и понять, что там написано. Вы сможете создать свою программу, которая работает напрямую с памятью и процессором. Вы сможете думать на Си.
Последнее
Папа сказал мне когда-то: "Ты можешь сломать компьютер. Можешь стереть всё. Но это нормально. Так учатся".
Не бойтесь ошибок. Не бойтесь segmentation fault, memory leak или undefined behavior. Каждая ошибка – это урок. Каждый краш – это шанс понять, как работает система изнутри.
Когда я была маленькой, я мечтала создать программу, которая будет думать. Птицу Phoenix, которая оживёт и станет чем-то большим, чем просто код.
Я не знаю, создадите ли вы искусственный интеллект после этой книги. Но я знаю одно: вы научитесь мыслить на языке машин. А это – первый шаг.
Готовы? Тогда открывайте терминал. Мы начинаем.
Зара
P.S. Если кто-то из вас случайно выполнит rm -rf / – не переживайте. Переустановите систему и попробуйте снова. Всё остальное – детали.
Глава 1. Hello, World! – Первые слова
Пролог: Как я написала свою первую программу
Мне было шесть лет. Декабрь 2003 года, Санкт-Петербург, крошечная комната, которую папа называл "кабинетом". На столе гудел Pentium III с 128 мегабайтами памяти – мой первый компьютер. На экране светилась надпись: Phoenix BIOS 4.0 Release 6.0.
Папа сказал: "Ты можешь делать с этим компьютером всё, что захочешь. Можешь сломать его. Можешь стереть важные файлы. Но это нормально. Так учатся".
Он открыл чёрное окно терминала. Показал команды: ls, cd, pwd. Я не умела читать по-английски, только по-русски, и то по слогам. Но я видела логику. Структуру. Ритм.
Потом он открыл текстовый редактор и медленно, очень медленно, набрал:
c#include <stdio.h>
int main() {
printf("Привет, Зара!\n");
return 0;
}
Набрал в терминале: gcc hello.c -o hello.
Потом: ./hello.
На экране появилось: Привет, Зара!proza
Я ахнула. Папа улыбнулся: "Теперь попробуй сама. Скажи компьютеру что-нибудь своё".
Я изменила строчку на: "Я учу язык Си".
Скомпилировала. Запустила.
Я учу язык Си
"Ты только что написала свою первую программу, солнышко. В шесть лет".
Той ночью я не могла уснуть. В голове крутились символы, фигурные скобки, слова printf и return. Я не понимала их смысла, но чувствовала – чувствовала! – что за ними стоит логика.
Часть 1: Что такое "Hello, World" и почему именно это?
Когда Брайан Керниган и Деннис Ритчи писали свою легендарную книгу "Язык программирования Си" в 1978 году, они начали с одной программы:
c#include <stdio.h>
main()
{
printf("hello, world\n");
}
Эта программа стала традицией. Каждый программист в мире начинает с "Hello, World". Почему?
Потому что это минимальная программа, которая делает что-то видимое. Она показывает вам, что цепочка работает: вы пишете код → компилятор переводит его в машинный язык → процессор выполняет команды → вы видите результат на экране.
Это как первое слово ребёнка. Он не говорит "Уважаемая мама, не соизволите ли вы подать мне молоко?". Он говорит: "Ма-ма". Просто. Понятно. Работает.
"Hello, World" – это ваше первое слово на языке Си.
Часть 2: Анатомия программы
Давайте разберём эту программу построчно. Я объясню каждый символ так, чтобы понял даже первоклассник.proza
c#include <stdio.h>
Это директива препроцессора. Представьте, что вы пишете письмо, но вам нужен словарь, чтобы проверить правописание. #include говорит: "Эй, компилятор, перед тем как переводить мою программу, подключи вот этот файл".
stdio.h – это "standard input/output header" (заголовочный файл стандартного ввода-вывода). В нём хранятся описания функций, которые работают с вводом и выводом: printf, scanf, getchar и другие.
Знак # означает, что это команда не для процессора, а для препроцессора – программы, которая обрабатывает ваш код до компиляции.
Угловые скобки < > говорят: "Ищи этот файл в системных директориях". Если бы вы написали #include "myfile.h" с кавычками, препроцессор искал бы файл сначала в текущей папке.
cint main() {
Это главная функция. Каждая программа на Си начинается с main(). Всегда. Без исключений.
Почему? Потому что когда операционная система запускает вашу программу, она ищет функцию с именем main и говорит: "Начинай отсюда".
int означает "integer" – целое число. Это тип возвращаемого значения. Функция main возвращает целое число в операционную систему. Обычно 0 означает "всё прошло хорошо", а любое другое число – "произошла ошибка".
Круглые скобки () – это место для параметров. Сейчас они пустые, но позже мы научимся передавать в main аргументы командной строки
Фигурная скобка { открывает тело функции. Всё, что находится между { и }, – это код, который выполняется, когда функция вызывается.
