Мыслим на Си
Мыслим на Си

Полная версия

Мыслим на Си

Настройки чтения
Размер шрифта
Высота строк
Поля
На страницу:
1 из 2

Зара Горенко

Мыслим на Си

Глава

Введение

Знаете, что общего у огненной птицы 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 / – не переживайте. Переустановите систему и попробуйте снова. Всё остальное – детали.

– Смотри, – сказал папа.

Он нажал на клавиатуре всего три буквы: v, i и Пробел. А потом написал имя файла, который мы будем создавать: h, e, l, l, o, ., c.

На экране получилось:

vi hello.c

Папа нажал большую кнопку Enter.

Черное окно терминала мигнуло и стало пустым. Внизу появились какие-то значки, но папа сказал не смотреть на них.

– Сейчас мы в режиме команд, – объяснил он. – Чтобы начать писать, нужно нажать волшебную кнопку.

Он нажал маленькую кнопку i. (Это значит Insert – вставка, объяснил он позже, но тогда я просто запомнила: "i" – это "играть", начало игры).

Теперь можно было писать. Папа печатал медленно, проговаривая каждую букву, и я следила за его пальцами. Он не просто нажимал кнопки, он строил дом из букв.

Сначала он набрал решетку. Для этого он зажал кнопку Shift и нажал цифру 3.

#

Потом без пробела написал слово "include" (включить).

i, n, c, l, u, d, e

Пробел.

Зажал Shift и нажал букву Б (там была скобочка <).

s, t, d, i, o, ., h

Зажал Shift и нажал букву Ю (скобочка >).

Нажал Enter, чтобы перейти на новую строку.

Написал:

i, n, t

Пробел.

m, a, i, n

Потом зажал Shift и нажал цифру 9, потом 0. Появились две круглые скобки: ( ).

Потом зажал Shift и нажал русскую букву Х, где была красивая фигурная скобка {.

Снова Enter.

Папа нажал кнопку Tab (длинную, слева), и курсор прыгнул немного вправо. Это чтобы было красиво.

Набрал:

p, r, i, n, t, f

Снова Shift + 9 (открыл скобку).

Снова Shift + Э (поставил кавычку ").

И тут он переключил язык на русский и набрал:

П, р, и, в, е, т, ,, , З, а, р, а, !

Потом он написал странный знак. Нажал кнопку над Enter-ом (слэш \), а потом букву n.

\n

– Это чтобы компьютер нажал Enter после слов, – пояснил папа.

Снова Shift + Э (закрыл кавычку ").

Снова Shift + 0 (закрыл скобку )).

И в конце, как точку в предложении, он поставил точку с запятой (нажал на букву Ж).

;

Нажал Enter.

Снова Tab.

Набрал:

r, e, t, u, r, n

Пробел.

0

И снова точка с запятой на букве Ж.

;

Нажал Enter.

Нажал Backspace (стер отступ, вернулся к левому краю).

И закрыл домик: зажал Shift и нажал русскую букву Ъ (закрывающая фигурная скобка }).

Текст на экране выглядел так:

#include

int main() {

printf("Привет, Зара!\n");

return 0;

}

Глава 1. Hello, World! – Первые слова

Пролог: Как я написала свою первую программу

Мне было шесть лет. Декабрь 2003 года, Санкт-Петербург, крошечная комната, которую папа называл "кабинетом". На столе гудел Pentium III с 128 мегабайтами памяти – мой первый компьютер. На экране светилась надпись: Phoenix BIOS 4.0 Release 6.0.

Папа сказал: "Ты можешь делать с этим компьютером всё, что захочешь. Можешь сломать его. Можешь стереть важные файлы. Но это нормально. Так учатся".

– Теперь самое сложное, – сказал папа серьезно. – Нам нужно выйти и сохранить.

Он нажал кнопку Esc (в самом углу слева сверху).

Потом зажал Shift и нажал букву Ж (двоеточие :).

Внизу экрана появилось двоеточие.

Папа нажал букву w (write – записать) и букву q (quit – выйти).

:wq

И ударил по Enter.

Текстовый редактор исчез. Мы снова были в черном терминале.

– Мы написали заклинание, – сказал папа. – Теперь нужно превратить его в настоящую программу. Это делает компилятор.

Он набрал:

g, c, c

Пробел.

h, e, l, l, o, ., c

Пробел.

–, o (минус и буква о)

Пробел.

h, e, l, l, o

gcc hello.c -o hello

И нажал Enter. Компьютер на секунду задумался, но ничего не ответил.

– Молчит – значит, всё хорошо, – улыбнулся папа. – Ошибок нет.

– А теперь, – он посадил меня к себе на колени, – нажми сама.

Он показал мне, что нажимать.

Точка ..

Косая черта / (слэш).

И имя нашей программы: h, e, l, l, o.

./hello

Я зажмурилась и нажала Enter.

На экране, белыми буквами на черном фоне, загорелось:

Привет, Зара!

Я ахнула. Папа улыбнулся: "Теперь попробуй сама. Скажи компьютеру что-нибудь своё".

Я изменила строчку на: "Я учу язык Си".

Скомпилировала. Запустила.

Я учу язык Си

"Ты только что написала свою первую программу, солнышко. В шесть лет".

Той ночью я не могла уснуть. В голове крутились символы, фигурные скобки, слова printf и return. Я не понимала их смысла, но чувствовала – чувствовала! – что за ними стоит логика.

Часть 1: Что такое "Hello, World" и почему именно это?

Когда Брайан Керниган и Деннис Ритчи писали свою легендарную книгу "Язык программирования Си" в 1978 году, они начали с одной программы:

c#include

main()

{

printf("hello, world\n");

}

Эта программа стала традицией. Каждый программист в мире начинает с "Hello, World". Почему?

Потому что это минимальная программа, которая делает что-то видимое. Она показывает вам, что цепочка работает: вы пишете код → компилятор переводит его в машинный язык → процессор выполняет команды → вы видите результат на экране.

Это как первое слово ребёнка. Он не говорит "Уважаемая мама, не соизволите ли вы подать мне молоко?". Он говорит: "Ма-ма". Просто. Понятно. Работает.

"Hello, World" – это ваше первое слово на языке Си.

Часть 2: Анатомия программы

Давайте разберём эту программу построчно. Я объясню каждый символ так, чтобы понял даже первоклассник.

c#include

Это директива препроцессора. Представьте, что вы пишете письмо, но вам нужен словарь, чтобы проверить правописание. #include говорит: "Эй, компилятор, перед тем как переводить мою программу, подключи вот этот файл".

stdio.h – это "standard input/output header" (заголовочный файл стандартного ввода-вывода). В нём хранятся описания функций, которые работают с вводом и выводом: printf, scanf, getchar и другие.

Знак # означает, что это команда не для процессора, а для препроцессора – программы, которая обрабатывает ваш код до компиляции.

Угловые скобки < > говорят: "Ищи этот файл в системных директориях". Если бы вы написали #include "myfile.h" с кавычками, препроцессор искал бы файл сначала в текущей папке.

cint main() {

Это главная функция. Каждая программа на Си начинается с main(). Всегда. Без исключений.

Почему? Потому что когда операционная система запускает вашу программу, она ищет функцию с именем main и говорит: "Начинай отсюда".

int означает "integer" – целое число. Это тип возвращаемого значения. Функция main возвращает целое число в операционную систему. Обычно 0 означает "всё прошло хорошо", а любое другое число – "произошла ошибка".

Круглые скобки () – это место для параметров. Сейчас они пустые, но позже мы научимся передавать в main аргументы командной строки

Фигурная скобка { открывает тело функции. Всё, что находится между { и }, – это код, который выполняется, когда функция вызывается.

cprintf("Hello, World!\n");

Это вызов функции printf. printf расшифровывается как "print formatted" – "печатать форматированно".

Что она делает? Выводит текст на экран.

"Hello, World!\n" – это строковый литерал. Двойные кавычки говорят: "Это текст, не код".

Символ \n – это escape-последовательность. Он означает "перевод строки" (newline). Без него курсор останется на той же строке после вывода, и следующий текст будет печататься сразу после "Hello, World!".

Точка с запятой ; – это конец оператора. В Си каждая команда должна заканчиваться точкой с запятой. Это как точка в конце предложения.

creturn 0;

Это оператор возврата. Он говорит: "Функция main закончила работу. Верни операционной системе число 0".

Почему 0? Потому что в Unix (и в Си) 0 означает "успех". Любое другое число означает ошибку. Это может показаться странным (обычно ноль ассоциируется с "ничем"), но в Unix-философии "ноль ошибок" – это хорошо.

c}

Закрывающая фигурная скобка. Конец функции main.

Часть 3: Компиляция – как превратить текст в программу

Вы написали код. Теперь нужно превратить его в программу, которую может выполнить процессор.

Компьютер не понимает Си. Он понимает только машинный код – последовательности нулей и единиц. Компилятор – это переводчик, который берёт ваш текст на Си и превращает его в инструкции для процессора.

Мы будем использовать компилятор GCC (GNU Compiler Collection). Это свободный компилятор, созданный в рамках проекта GNU Ричарда Столлмана.

Сохраните вашу программу в файл hello.c (расширение .c означает, что это код на Си).

Откройте терминал и введите:

bashgcc hello.c -o hello

Что происходит?

gcc – это команда запуска компилятора

hello.c – исходный файл (ваш код)

–o hello – опция "output" (выход). Говорит: "Назови скомпилированную программу hello"

Если всё прошло без ошибок, в вашей директории появится файл hello (или hello.exe в Windows).

Теперь запустите его:

bash./hello

На экране появится:

textHello, World!

Поздравляю! Вы только что написали, скомпилировали и запустили свою первую программу на Си.

Часть 4: Что происходит внутри?

Давайте заглянем под капот. Что на самом деле делает компилятор?

Шаг 1: Препроцессор

Препроцессор обрабатывает директивы #include, #define и другие. Он берёт содержимое файла stdio.h и вставляет его в начало вашей программы.

Вы можете увидеть результат работы препроцессора:

bashgcc -E hello.c

Вы увидите сотни строк кода – всё содержимое stdio.h. Ваша маленькая программа превратилась в огромный текстовый файл!

Шаг 2: Компиляция

Компилятор берёт препроцессированный код и переводит его в ассемблер – язык, близкий к машинному коду.

bashgcc -S hello.c

Откройте файл hello.s – это ассемблерный код вашей программы. Он выглядит странно, но если приглядеться, вы увидите инструкции процессора: mov, call, ret.

Шаг 3: Ассемблирование

Ассемблер переводит ассемблерный код в объектный файл – почти готовую программу, но ещё не полную.

bashgcc -c hello.c

Появится файл hello.o – объектный файл. Если вы попытаетесь открыть его текстовым редактором, увидите бессмысленные символы – это машинный код.

Шаг 4: Линковка (компоновка)

Линкер (компоновщик) берёт ваш объектный файл и соединяет его с библиотеками – например, с кодом функции printf. Только теперь программа полностью готова.

Весь этот процесс выполняется автоматически, когда вы пишете gcc hello.c -o hello.

Часть 5: Эксперименты – изменяйте и ломайте

Помните, что сказал мне папа? "Ты можешь сломать его. Но это нормально. Так учатся".

Попробуйте изменить программу:

Эксперимент 1: Другой текст

cprintf("Меня зовут Зара. Я изучаю Си.\n");

Эксперимент 2: Несколько строк

cprintf("Первая строка\n");

printf("Вторая строка\n");

printf("Третья строка\n");

Эксперимент 3: Уберите \n

cprintf("Hello, World!");

Что изменится?

Эксперимент 4: Специальные символы

cprintf("Табуляция:\tвот так\n");

printf("Кавычки: \"текст в кавычках\"\n");

printf("Обратный слеш: \\\n");

Эксперимент 5: А теперь сломайте!

Уберите точку с запятой:

cprintf("Hello, World!\n")

Скомпилируйте. Что скажет компилятор?

Уберите фигурную скобку:

cint main() {

printf("Hello, World!\n");

return 0;

Уберите return 0;:

cint main() {

printf("Hello, World!\n");

}

(Это, кстати, сработает в новых версиях Си – стандарт C99 добавил неявное return 0; в конце main).

Читайте ошибки компилятора. Он говорит вам, где проблема. Он ваш учитель.

Часть 6: Философия первой программы

Когда Линус Торвальдс написал первую версию Linux, он начал с простейшей программы, которая просто выводила символ на экран. Не многозадачность, не файловые системы – просто "А" на экране.

Потом он добавил следующий символ. Потом – следующий. Шаг за шагом. Строчка за строчкой.

Сегодня Linux – это миллионы строк кода, работающие на миллиардах устройств. Но всё началось с одного символа.

"Hello, World" – это не просто учебная программа. Это философия. Начинай с малого. Проверь, что цепочка работает. Потом добавляй сложность.otvet.

В Unix есть принцип: "Делай одну вещь, но делай её хорошо". printf делает одно – выводит текст. Но делает это идеально.

Заключение главы

Вы написали свою первую программу. Вы скомпилировали её. Запустили. Изменили. Сломали. Починили.

Теперь вы знаете:

Что такое #include и зачем он нужен

Что такое функция main и почему она главная

Как работает printf

Что такое компиляция и как превратить код в программу

Почему return 0; означает успех

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

Но прежде чем идти дальше, напишите ещё пять программ. Просто так. Пусть они выводят ваше имя, ваш возраст, ваше любимое число. Пусть они говорят что-то смешное или грустное.

Компьютер слушает вас. Говорите с ним.

Практическое задание:

Напишите программу, которая выводит ваше имя, возраст и любимую книгу. Каждая строка – отдельный printf. Скомпилируйте и запустите.

Потом измените код так, чтобы всё было в одном printf с символами \n для переноса строк.

Сохраните эту программу. Она – ваш первый шаг. Когда-нибудь вы посмотрите на неё и улыбнётесь: "С этого всё начиналось".

Часть 6 (продолжение Главы 1): Как подготовить компьютер к экспериментам

Компилятор – ваш переводчик

Прежде чем писать код, нужно понять: компьютер не понимает Си. Он понимает только машинный код – последовательности нулей и единиц, инструкции для процессора.

Компилятор – это программа, которая переводит ваш код на Си в машинный код. Без компилятора ваша программа – просто текст. С компилятором – это команды для процессора.

Мы будем использовать GCC (GNU Compiler Collection). Это свободный компилятор, созданный Ричардом Столлманом в рамках проекта GNU. На нём скомпилировано всё: от ядра Linux до программ на вашем Android-смартфоне.

Вариант 1: У вас Linux (идеальный случай)

Если у вас уже установлен Linux (Ubuntu, Mint, Debian, Fedora) – поздравляю, вы готовы.

Откройте терминал (обычно Ctrl + Alt + T) и проверьте:

bashgcc –version

Если видите что-то вроде:

textgcc (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0 Copyright (C) 2021 Free Software Foundation, Inc.

Значит, GCC уже установлен. Можно сразу писать код.

Если команда не найдена, установите:

bash# Ubuntu, Debian, Mint sudo apt update sudo apt install build-essential # Fedora sudo dnf install gcc # Arch Linux sudo pacman -S gcc

Команда build-essential установит не только GCC, но и другие необходимые инструменты: make, g++, библиотеки.r-5

Вариант 2: У вас Windows (но хотите Linux)

Windows не создан для разработки на Си. Си создан для Unix. Но есть несколько способов получить Unix-окружение в Windows:

Способ 2.1: WSL (Windows Subsystem for Linux) – рекомендую!

WSL – это настоящий Linux внутри Windows. Не виртуальная машина, не эмулятор – полноценное ядро Linux, интегрированное в Windows.

Установка (5 минут):

Откройте PowerShell от имени администратора: Нажмите Win + X Выберите "Windows PowerShell (администратор)" или "Терминал (Администратор)"

Введите одну команду:

powershellwsl –install

Подождите 5-10 минут (загружается Ubuntu)

Перезагрузите компьютер

После перезагрузки откроется окно Ubuntu – создайте имя пользователя и пароль

Установите GCC:

bashsudo apt update sudo apt install build-essential

Готово! Теперь у вас Linux в Windows.

Открыть WSL можно через меню Пуск → Ubuntu, или в любой папке в Проводнике: правая кнопка → "Open in Terminal".

Способ 2.2: Загрузочная флешка – попробовать без установки

Не хотите менять систему? Запустите Linux с флешки!

Что нужно:

USB-флешка 8 ГБ или больше

Программа UNetbootin или balenaEtcher

Как сделать (10 минут):

Скачайте UNetbootin: unetbootin.github.io

Запустите программу

Выберите: Distribution: Ubuntu Version: последняя доступная Type: USB Drive Drive: буква вашей флешки

Нажмите OK

Программа сама скачает Ubuntu и запишет на флешку

Подождите 10-15 минут

Загрузка с флешки:

Вставьте флешку

Перезагрузите компьютер

При включении нажимайте F12, F11, F9 или Esc (зависит от производителя)

В меню загрузки выберите USB-флешку

В меню Ubuntu выберите "Try Ubuntu without installing"

Через минуту загрузится рабочий стол Linux

Откройте терминал (Ctrl + Alt + T) и установите GCC:

bashsudo apt update sudo apt install build-essential

Теперь можно писать код! При перезагрузке всё вернётся к исходному состоянию – можно экспериментировать и ломать систему без последствий.

Для сохранения файлов между сеансами: в UNetbootin при создании флешки установите "Persistent partition size" (например, 4096 МБ). Теперь ваши программы и файлы будут сохраняться на флешке.

Вариант 3: У вас macOS

macOS основана на BSD Unix, поэтому отлично подходит для Си.

Установка:

Откройте Terminal (Cmd + Space → введите "Terminal")

Установите Xcode Command Line Tools:

bashxcode-select –install

Появится окно – нажмите "Установить"

Подождите 5-10 минут

Проверьте:

bashgcc –version

Готово! Можно писать код.

Вариант 4: Онлайн-компилятор (если ничего не хочется устанавливать)

Можно писать код прямо в браузере – без установок:

Рекомендую:

Replit: replit.com – выберите язык C, нажмите "Create Repl"

OnlineGDB: onlinegdb.com – простой компилятор с отладчиком

Пишете код → нажимаете "Run" → программа выполняется на сервере.

Плюсы: ничего не нужно устанавливать, работает даже на планшете.

Минусы: нет доступа к системным вызовам, ограниченное время выполнения.

Часть 7: Первый эксперимент – проверяем, что всё работает

На страницу:
1 из 2