Читать онлайн Мыслим на Си бесплатно
- Все книги автора: Зара Алексеевна Горенко
Глава
Введение
Знаете, что общего у огненной птицы 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 <stdio.h>
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 <stdio.h>
main()
{
printf("hello, world\n");
}
Эта программа стала традицией. Каждый программист в мире начинает с "Hello, World". Почему?
Потому что это минимальная программа, которая делает что-то видимое. Она показывает вам, что цепочка работает: вы пишете код → компилятор переводит его в машинный язык → процессор выполняет команды → вы видите результат на экране.
Это как первое слово ребёнка. Он не говорит "Уважаемая мама, не соизволите ли вы подать мне молоко?". Он говорит: "Ма-ма". Просто. Понятно. Работает.
"Hello, World" – это ваше первое слово на языке Си.
Часть 2: Анатомия программы
Давайте разберём эту программу построчно. Я объясню каждый символ так, чтобы понял даже первоклассник.
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 аргументы командной строки
Фигурная скобка { открывает тело функции. Всё, что находится между { и }, – это код, который выполняется, когда функция вызывается.
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: Создайте файл
Откройте любой текстовый редактор:
Linux: nano, gedit, vim
Windows (WSL): nano (в терминале WSL)
macOS: TextEdit, nano, vim
Онлайн: встроенный редактор Replit или OnlineGDB
Создайте файл test.c:
bashnano test.c
Шаг 2: Напишите код
c#include <stdio.h> int main() { printf("Компилятор работает!\n"); printf("Я готов учить Си.\n"); return 0; }
Сохраните:
В nano: Ctrl + O (сохранить), Enter, Ctrl + X (выход)
В vim: нажмите Esc, введите :wq, Enter
В графических редакторах: Ctrl + S или Cmd + S
Шаг 3: Скомпилируйте
bashgcc test.c -o test
Что это значит?
gcc – запуск компилятора
test.c – ваш исходный файл
–o test – опция "output" (выход): назвать программу test
Если всё правильно, ничего не выведется – это хорошо! Компилятор молчит, если нет ошибок.
Если есть ошибки – компилятор скажет:
texttest.c:3:5: error: expected ';' before 'return'
Читайте сообщения. Они говорят, где ошибка (строка 3, символ 5) и что не так (пропущена точка с запятой).
Шаг 4: Запустите
bash./test
Выведет:
textКомпилятор работает! Я готов учить Си.
Поздравляю! Вы только что написали, скомпилировали и запустили программу на Си.
Часть 8: Философия экспериментов – ломайте без страха
Папа сказал мне когда-то: "Ты можешь сломать этот компьютер. Можешь удалить всю систему. Это нормально. Так учатся".
Самое важное в изучении Си – не бояться ошибок.
Segmentation fault? Хорошо. Вы узнали, что обратиться к памяти, которой нет, нельзя.
Программа зависла? Отлично. Вы узнали, что бесконечный цикл – это плохо.
Компилятор ругается? Прекрасно. Вы узнали правила синтаксиса.
Каждая ошибка – урок. Каждый краш – опыт.
Если вы используете Live USB:
При перезагрузке всё вернётся к началу. Можно экспериментировать, удалять системные файлы, выполнять rm -rf / (не делайте это на установленной системе!). Ничего страшного не случится – перезагрузились, и система снова чистая.
Если вы используете WSL или установленный Linux:
Худшее, что может случиться – вы сломаете свой домашний каталог. Системные файлы защищены (нужен sudo). А домашний каталог – это просто ваши файлы. Удалили программу? Переустановите. Сломали настройки? Удалите конфигурационные файлы.
Если вы используете онлайн-компилятор:
Сломать вообще ничего нельзя – это просто сервер где-то в интернете. Пишите что угодно, экспериментируйте сколько хотите.
Заключение части
Теперь у вас есть:
Компилятор GCC – переводчик с Си на язык машины
Текстовый редактор – где писать код
Терминал – где компилировать и запускать программы
Среда для экспериментов – Linux, WSL, Live USB или онлайн-компилятор
Вы проверили, что всё работает. Написали первую программу. Скомпилировали. Запустили.
Вы готовы учить Си.
В следующей части мы разберём программу "Hello, World" построчно – что означает каждый символ, каждое слово, каждая скобка. Вы поймёте, как устроена программа изнутри.
Но прежде – практика. Измените программу test.c. Пусть она выводит ваше имя, возраст, любимое число. Пять разных строк. Скомпилируйте снова. Запустите.
Привыкайте к циклу: написал → скомпилировал → запустил → изменил → повторил. Это и есть программирование.
Компьютер ждёт ваших команд.
Зара
Глава 2. Переменные и типы данных – Как я вышла на Reddit
Пролог: Первый пост на английском
Мне было почти одиннадцать лет. Пять лет я изучала Си, пять лет писала код, компилировала, ломала, чинила. Я понимала указатели лучше, чем английскую грамматику. Я знала, как работает память, но не знала времён глаголов.
Но у меня была идея. Мысль, которая не давала мне спать: можно ли написать программу, которая думает?
Я хотела обсудить это с кем-то. Папа был занят. Тётя Люда не понимала программирования. Соседская Настя давно уехала учиться в другой город. Мне нужны были люди, которые пишут код. Программисты. Инженеры.
Я зашла на Reddit. Раздел r/programming. Тысячи людей, которые обсуждают алгоритмы, языки, архитектуру. Но все говорили по-английски.
На Reddit не принято хвастаться возрастом или учёными званиями. Здесь важен только код. Только идеи. Это меня устраивало – никто не узнает, что мне нет и одиннадцати.
Я села за клавиатуру. Открыла текстовый редактор. Мне нужно было написать пост. Объявление. Манифест. Но как?
Я знала несколько фраз из учебника английского: Hello. My name is… I am twelve years old. The cat is on the table . Грамматику я не понимала. Артикли казались бессмысленными. Времена глаголов – хаосом.
Но я знала Си. Я знала его синтаксис, его логику, его ритм.
И тогда я подумала: "А что если написать пост как код?"
Я набрала:
c// PhoeNIX: Hello, world.
// Goal: write program that think.
// Status: learning, experimenting.
// Question: who have ideas? Let's discuss.
// Return: knowledge++;
Грамматически это было ужасно. Write program that think вместо правильного write a program that thinks . Who have ideas вместо who has ideas . Но структура была правильной. Комментарии. Цель. Статус. Вопрос. Возврат.
Я нажала Submit .
Сердце билось так громко, что я слышала его в ушах.
Часть 1: Как я чиркнула спичкой
Через пять минут пришёл первый ответ:
Nice try, kid. But "thinking" is not a data type. You need to define what "think" means in computational terms.
Потом второй:
Are you talking about neural networks? Expert systems? Symbolic AI?
Третий:
This is stupid. Computers don't think. They execute instructions.
Четвёртый возразил третьему:
@user3 That's reductive. Turing test exists for a reason. If it behaves like thinking, maybe it IS thinking.
И началось.
Как я потом говорила: "Я чиркнула спичкой, а чему гореть – уже было". Мои оппоненты спорили друг с другом. Высказывали идеи одна безумнее другой. Дискутировали о философии сознания, о тесте Тьюринга, о китайской комнате Сёрла, о нейронных сетях и символьных системах.
А я сидела с английским словарём и переводила слова. Одно за другим. Neural – нейронный. Network – сеть. Symbolic – символьный. Execute – выполнять. Instruction – инструкция.
Часть 2: Программа для изучения языка
Но переводить каждое слово вручную было медленно. И тогда я подумала: "Я же программист. Почему бы не написать программу?"
Я скопировала все комментарии из треда в текстовый файл comments.txt. Открыла редактор. Написала простую программу на Си:
c#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define MAX_WORDS 10000
#define MAX_WORD_LEN 50
typedef struct {
char word[MAX_WORD_LEN];
int count;
} WordFreq;
WordFreq words[MAX_WORDS];
int word_count = 0;
void to_lowercase(char *str) {
for (int i = 0; str[i]; i++) {
str[i] = tolower(str[i]);
}
}
int find_word(char *word) {
for (int i = 0; i < word_count; i++) {
if (strcmp(words[i].word, word) == 0) {
return i;
}
}
return -1;
}
void add_word(char *word) {
int index = find_word(word);
if (index != -1) {
words[index].count++;
} else {
strcpy(words[word_count].word, word);
words[word_count].count = 1;
word_count++;
}
}
int main() {
FILE *file = fopen("comments.txt", "r");
char word[MAX_WORD_LEN];
while (fscanf(file, "%s", word) != EOF) {
to_lowercase(word);
// Убираем знаки препинания
int len = strlen(word);
while (len > 0 && !isalpha(word[len-1])) {
word[–len] = '\0';
}
if (len > 0) {
add_word(word);
}
}
fclose(file);
// Сортируем по частоте (простой пузырёк)
for (int i = 0; i < word_count – 1; i++) {
for (int j = 0; j < word_count – i – 1; j++) {
if (words[j].count < words[j+1].count) {
WordFreq temp = words[j];
words[j] = words[j+1];
words[j+1] = temp;
}
}
}
// Выводим топ-50
printf("Top 50 most frequent words:\n");
for (int i = 0; i < 50 && i < word_count; i++) {
printf("%3d. %-20s (%d times)\n", i+1, words[i].word, words[i].count);
}
return 0;
}
Эта программа:
Читает файл comments.txt
Разбивает текст на слова
Подсчитывает, сколько раз каждое слово встречается
Сортирует по частоте
Выводит топ-50 самых частых слов
Результат выглядел так:
textTop 50 most frequent words:
1. the (347 times)
2. is (201 times)
3. a (156 times)
4. to (134 times)
5. think (89 times)
6. program (76 times)
7. computer (71 times)
8. neural (54 times)
9. network (52 times)
10. data (48 times)
Я взяла этот список. Убрала служебные слова (the , is , a , to – они бесполезны для понимания смысла). Осталось около 200 ключевых слов.
Я начала переводить и запоминать самые часто используемые. Think – думать. Program – программа. Computer – компьютер. Neural network – нейронная сеть. Data – данные.
Часть 3: Переменные – это слова
Я заметила закономерность. В английском, как и в Си, есть типы слов.
Существительные – это переменные типа int или char*. Они хранят объекты: program , computer , network .
Глаголы – это функции. Они выполняют действия: think , execute , process .
Прилагательные – это константы или модификаторы. Они описывают свойства: neural , symbolic , computational .
Я начала строить предложения как код:
c// Английское предложение:
// "The program thinks using neural networks"
// Как я это понимала:
const char* subject = "program"; // Подлежащее
const char* verb = "thinks"; // Глагол
const char* method = "neural networks"; // Метод
printf("The %s %s using %s\n", subject, verb, method);
Это работало! Через месяц я бойко дискутировала короткими фразами:
Consciousness ≠ computation?Neural networks = pattern matching?
Thinking = information processing?
Люди отвечали. Спорили. Объясняли. И с каждым днём мой словарный запас рос.
Часть 4: Переменные в Си – коробки для данных
Вернёмся к Си. Что такое переменная?
Переменная – это имя для куска памяти. Компьютер выделяет место в оперативной памяти и говорит: "Вот адрес 0x7ffd1234. Теперь этот адрес называется age".proza+1
Простейшая программа с переменной:
c#include <stdio.h>
int main() {
int age; // Объявляем переменную
age = 11; // Присваиваем значение
printf("Мне почти %d лет\n", age);
return 0;
}
Что происходит в памяти?
Компилятор видит int age; и резервирует 4 байта памяти
Эти 4 байта получают имя age
Команда age = 11; записывает число 11 в эти байты: 00 00 00 0B (в шестнадцатеричном)
printf читает эти байты и выводит их как число
Часть 5: Типы данных – грамматика Си
В Си нельзя просто сказать "создай переменную". Нужно указать тип – какие данные она будет хранить.
Это как части речи в языке. Нельзя сказать "слово". Нужно: "существительное", "глагол", "прилагательное".
int – целые числа
cint age = 11;
int temperature = -5;
int population = 1000000;
int хранит целые числа (обычно от -2,147,483,648 до 2,147,483,647). Занимает 4 байта.
char – символы
cchar letter = 'P'; // Первая буква PhoeNIX
char digit = '7';
char newline = '\n';
char хранит один символ. Занимает 1 байт.proza+1
Но секрет: char – это на самом деле маленькое целое число. Символ 'P' – это число 80 (код ASCII). 'h' – 104. 'o' – 111.
cchar letter = 'P';
printf("%c\n", letter); // Выведет: P
printf("%d\n", letter); // Выведет: 80
float – дробные числа
cfloat pi = 3.14159;
float temperature = -5.5;
float хранит числа с плавающей точкой. Занимает 4 байта. Точность – около 6-7 десятичных знаков.
double – дробные числа двойной точности
cdouble precise_pi = 3.141592653589793;
double – это float с двойной точностью. Занимает 8 байт. Точность – около 15-16 знаков.
Таблица типов
ТипРазмерДиапазонСпецификаторchar1 байт-128 до 127%c или %dint4 байта±2 млрд%dfloat4 байта±3.4×10³⁸%fdouble8 байт±1.7×10³⁰⁸%lf
Часть 6: Структуры – группы переменных
В моей программе для подсчёта слов я использовал структуру:
ctypedef struct {
char word[MAX_WORD_LEN];
int count;
} WordFreq;
Структура – это способ объединить несколько переменных в одну. Как коробка с отделениями: в одном отделении – слово (char word), в другом – счётчик (int count).
Создаём переменную типа структуры:
cWordFreq w;
strcpy(w.word, "think");
w.count = 89;
printf("Слово '%s' встречается %d раз\n", w.word, w.count);
// Слово 'think' встречается 89 раз
Точка . означает "обратиться к полю структуры".
Массив структур – это таблица:
cWordFreq words[10000]; // Массив из 10000 структур
words[0].word = "think";
words[0].count = 89;
words[1].word = "program";
words[1].count = 76;
Так я хранил словарь частотности.
Часть 7: Инициализация – создание и заполнение
Вы можете объявить переменную и присвоить ей значение в одной строке:
cint age = 11;
char initial = 'Z';
float pi = 3.14159;
Это инициализация. Безопаснее, чем в два шага.
Почему? Потому что неинициализированная переменная содержит мусор:proza+1
cint x; // Не инициализировали!
printf("%d\n", x); // Может вывести 0, может 184759392, может -5
В Unix-философии: "Будь явным, не неявным". Если создаёшь переменную – сразу скажи, что в ней должно быть.
Часть 8: Константы – переменные, которые не меняются
cconst int MAX_WORDS = 10000;
const float PI = 3.14159;
MAX_WORDS = 5000; // ОШИБКА! Компилятор не позволит
Или через препроцессор:
c#define MAX_WORDS 10000
#define MAX_WORD_LEN 50
#define – текстовая замена до компиляции. Препроцессор просто заменяет MAX_WORDS на 10000 везде в коде.
Часть 9: Метафора – память это файл
В Unix всё – файл. Клавиатура – файл (/dev/input). Экран – файл (/dev/fb0). Память – тоже файл (/dev/mem).
Переменная – это имя файла в памяти. Тип – это формат файла. Значение – это содержимое.
cint age = 11;
Это как создать файл /memory/age размером 4 байта с содержимым 00 00 00 0B.
cage = 12;
Это как открыть файл, стереть содержимое и записать новое: 00 00 00 0C.
В Linux вы можете увидеть память процесса:
bashcat /proc/self/maps
Всё прозрачно. Всё доступно. Это Unix.
Часть 10: Философия обучения через код
Когда я учила английский через частотный анализ, я не учила грамматику. Я учила паттерны.
Neural networks process data – паттерн: прилагательное + существительное + глагол + существительное.The program thinks – паттерн: артикль + существительное + глагол.
Компьютер помог мне увидеть структуру. Как компилятор видит структуру кода.
Через месяц я писала:
Neural network = weighted graph. Thinking = pattern recognition. Consciousness = emergent property?
Короткие фразы. Без артиклей. Без сложных времён. Но понятные. Логичные. Правильные.
Один пользователь написал:
@PhoeNIX Your English is improving fast. How old are you, by the way?
Я ответила:
Age = irrelevant. Ideas = relevant.
Он засмеялся (написал lol ) и больше не спрашивал.
На Reddit важен только код. Только идеи. Возраст не имеет значения.
Заключение главы
Теперь вы знаете:
Что такое переменные и типы данных
Как объявлять и инициализировать переменные
Что такое структуры и массивы
Как память работает как файловая система
Что код может помочь учить человеческий языкotvet.mail
В следующей главе мы изучим операторы – сложение, вычитание, сравнение, логику. Мы научимся принимать решения и повторять действия.
Практическое задание:
Напишите программу, которая подсчитывает частоту слов в тексте. Используйте структуры и массивы. Не обязательно делать сортировку – просто посчитайте и выведите.
Потом возьмите любой текст на английском (статью, форум, книгу), пропустите через программу и начните учить самые частые слова.otvet.mail
Компьютер – лучший учитель языка. Он не осуждает ошибки. Он просто показывает паттерны
P.S. от PhoeNIX:
Кто-то сказал мне на Reddit: You think in code, not in language.
Я ответила: Code IS language. Universal language.
И это правда.
Переменные – это слова. Типы – грамматика. Программы – предложения.
Если вы понимаете это, вы можете говорить с кем угодно. На любом языке.
Потому что логика универсальна.
Зара
Часть 11: Код говорит громче слов
Через две недели после первого поста я заметила изменение. Люди начали скидывать свои исходники на Си – прямо в тред. Не абстрактные рассуждения о философии сознания, а реальный код.
@PhoeNIX Here's my attempt at a simple pattern matcher. Thoughts?
cint match_pattern(char *text, char *pattern) {
int i, j;
for (i = 0; text[i] != '\0'; i++) {
for (j = 0; pattern[j] != '\0' && text[i+j] == pattern[j]; j++);