4.02. Что такое код и как он работает
Разработчику
Аналитику
Тестировщику
Архитектору
Инженеру
Понятие кода
Прежде, чем перейдём к коду, давайте сразу подчеркнём важнейший момент.
Существует два подхода к логике работы программ - алгоритмический язык и собственно реальный язык кода или программирования.
В голове мы рисуем себе некий алгоритм, когда формируем и декомпозируем задачу. На бумаге (в коде) же мы отражаем уже готовый код, который мы пишем строго на условиях и правилах, принятых в соответствующем языке программирования. И да, программирование выполняется на английском языке, и большинство выражений в языках означают прямо то, что они подразумевают в прямом переводе (например, «if» - «если»). Поэтому знание английского языка будет не лишним в IT, и значительно поможет формулировать логику.
Алгоритмический язык же пишется в том порядке, как мы изучили алгоритмы - мы пишем всё так, как понимаем - определяем шаги, и пишем на языке понимания, к примеру ЕСЛИ <условие> ТО <действие> ИНАЧЕ <другое действие>. Потом мы превращаем это в код - IF <условие> THEN <действие> ELSE <другое действие> (с учётом правил соответствующего языка).
★ Код – это набор инструкций, написанных на языке программирования, который преобразуется в действия компьютера.
★ Блок кода – логически связанная группа инструкций. Обычно выделяется отступами (Python) или фигурными скобками (Java/C#):
{ всё что между скобками – блок кода }
или так:
это начало блока
это конец блока
Блок может быть частью условия, цикла, функции или любого другого контекста.
Вложенность (nesting) — это ситуация, когда один блок находится внутри другого - так можно выполнять одни, к примеру, условные действия, внутри других условий, для обработки данных внутри циклов, чтобы строить сложную логику «если А, то проверь Б, и если Б верно, сделай В».
{
блок кода {
ещё блок {
ещё блок {
...и так сколько угодно;
}
}
}
}
Разные языки используют разные способы указания начала и конца блока, к примеру, в большинстве языков блок выделяется символами { и }, а Python - отступами.
★ Кодирование – процесс написания исходного кода по определённым правилам языка. Код может писаться в любом текстовом редакторе, либо сразу в специальных программах, которые обладают дополнительными возможностями для удобной работы с кодом.
Важно отличать кодирование/декодирование от кодирования в контексте программирования. Само по себе кодирование — это широкое понятие, включающее в себя простой процесс - превращения чего-то исходного в код. Декодирование - наоборот, превращение кода во что-то другое.
И в нашем случае, мы говорим о том, что кодирование — это написание исходного кода на конкретном языке программирование. Код пишется на языке, понятном программисту. Но как машина понимает этот код, она что, знает английский? Не совсем. Специальный инструмент, называемый компилятором, превращает этот код, понятный программисту, в код, понятный машине - машинный код.
★ Компиляция – преобразование всего исходного кода в машинный код до запуска программы по принципу:
Исходный код → Компилятор → Исполняемый файл → Процессор
Виды и особенности компиляции:
- AOT-компиляция (Ahead Of Time) – код компилируется перед запуском полностью (C, C++);
- JIT-компиляция (Just In Time) – код компилируется во время выполнения (Java, C#);
- Кросс-компиляция – компиляция кода для другой платформы, например, при компиляции кода для среды Android в Windows.
Таким образом, компилятор берёт исходные данные (код - исходный код) и выполняет свою собственную задачу - конвертацию (преобразование), превращение. Компилятор — это тоже программа.



★ Интерпретация – построчное выполнение кода без предварительной компиляции, когда интерпретатор читает и выполняет код строку за строкой. Это медленнее компиляции (из-за построчного выполнения и отсутствия оптимизации на этапе компиляции), но не требует отдельного этапа компиляции, работая по принципу:
Исходный код → Интерпретатор → Выполнение
Виды и особенности интерпретации:
- Полная интерпретация – каждая строка переводится в машинный код при каждом запуске (на старых версиях BASIC);
- Байт-код + Виртуальная машина – код сначала компилируется в байт-код, а затем выполняется виртуальной машиной (Python);
- JIT-интерпретация (Just In Time) – байт-код компилируется в машинный код на лету (например, JavaScript в V8 – специальном движке в Google).



Многие языки используют гибридный подход. В Java код компилируется в байт-код, а затем интерпретируется виртуальной машиной. Это распространённый способ.
Интерпретируемый язык выполняет свои операторы в порядке «строка за строкой». Такие языки, как Python, Javascript, R, PHP и Ruby, являются яркими примерами интерпретируемых языков. Программы, написанные на интерпретируемом языке, запускаются непосредственно из исходного кода, без промежуточного этапа компиляции.
★ Трансляция – общее название для преобразования кода из одной формы в другую. Включает компиляцию в машинный код и транспиляцию (из языка в язык, например TypeScript → JavaScript).
Здесь важно отметить, что это понятие не нужно путать с компиляцией и интерпретацией. В ряде случев, может существовать некая «надстройка» над языком, которая проверяет сначала свои правила, а затем уже выполняет интерпретацию или компиляцию. Как раз TypeScript сам по себе не язык программирования, а надстройка над JavaScript, и имеет свои требования к синтаксису. TS сначала проверяет по своим правилам, а затем выполняется транспиляция в JavaScript, который уже выполняется как обычно.
Такие надстройки называют суперсет (superset), которые расширяют базовый язык, добавляя новые возможности, но оставаясь совместимым с ним, имея при этом свои собственные правила и синтаксис.
★ Машинный код – текст программы, понятный компьютеру. Фактически, там уже вовсе не текст, а набор сигналов. Программа понимает по принципу «есть сигнал» или «нет сигнала».
★ Байт-код – компактное представление программы, но читаемое не процессором, в отличие от машинного кода, а виртуальной машиной – интерпретатором. Длина каждого кода операции составляет один байт.
★ Исходный код – текст программы, написанный программистом, который затем преобразуется в исполняемый файл.
Если сравнить их, то будет такая картина:
| Код | Кому предназначен |
|---|---|
| Машинный код 💻 | Предназначен для устройства – процессора |
| Байт-код 🔩 | Предназначен для движка, платформы или виртуальной машины |
| Исходный код 📝 | Предназначен для чтения программистом |
Когда компилятор или интерпретатор анализирует исходный код, он сначала строит конкретное синтаксическое дерево (КСД) — точное отражение грамматики языка. Но оно слишком детализировано и содержит много лишней информации, не влияющей на смысл программы.
Абстрактное синтаксическое дерево (АСД) — это структура данных, представляющая семантическую структуру исходного кода программы после его разбора (парсинга), без учёта синтаксических деталей, таких как скобки, точки с запятой, ключевые слова. АСД — это упрощённая, «очищенная» версия КСД, в которой остаются только существенные для семантики элементы: операторы, выражения, переменные, вызовы функций и т.д.
У каждого кода есть некий набор заранее подготовленных слов, которые являются синтаксическими конструкциями, иначе – синтаксисом – это правила, которые определяют, как писать код на определенном языке.
Синтаксис включает в себя ключевые слова, операторы, символы и структуры данных. Правила синтаксиса очень важны.
Ключевые слова — это зарезервированные слова, которые имеют специальное значение в языке. Например, if используется для условных выражений, а for для циклов. Эти слова нельзя использовать для других целей, таких как имена переменных или функций, так как они уже заняты самим языком. Если использовать неверное ключевое слово - будет ошибка.
Символы — это важный элемент любого кода. Например, угловые скобки где-то могут означать сравнения (<, >), а где-то - начало и конец тега (<это тег>). Но самые важные символы в коде — это запятая (,) и точка с запятой (;). Если нарушить правила синтаксиса и забыть поставить точку с запятой в коде, программа не скомпилируется или завершится с ошибкой.
Операторы — это специальные выражения, которые определяют поведение или условие. Соответственно, оператор с условием — это условный оператор. В различных языках используются условные операторы.
★ Условный оператор – контроль потока выполнения программы, позволяющий выбрать действие в зависимости от условия. Важно понимать, что такое оператор, операция и операнд.

★ Операция – действие, например, суммирование.
★ Операнд – данные, над которым выполняется операция (в a+b, a и b – операнды).
★ Оператор – символ или ключевое слово, выполняющее операцию (if, for, =, +).
Таким образом, в выражении «a + b»:
a+bэто операция суммирования;- a и b это операнды;
-
- это оператор.
Структуры данных мы уже изучили ранее, поэтому на них мы останавливаться не будем.
И между ключевыми словами, которые зарезервированы в языках, есть некие указатели данных - ведь в нашем примере a и b — это не сами данные, а лишь именованные элементы. Мы не суммируем буквы переменных, мы суммируем то, чему равняется a и b. Это - переменные.
★ Переменные – именованная область памяти, которая хранит данные. Её можно представить как коробку с названием, в которой лежит значение. Это основной инструмент для работы с данными в программировании. Например, мы можем создать переменную age и сохранить в неё возраст пользователя и использовать это значение в дальнейших вычислениях.
★ Имя переменной (идентификатор) – должно быть уникальным, часто регистрозависимым (например, name и Name – разные переменные). Имя может состоять из букв, цифр и символов подчёркивания, но не может начинаться с цифры.
★ Тип данных – переменные могут хранить числа, строки, логические значения и другое. Их можно разделить на:
- Числа - целые числа, числа с плавающей точкой, комплексные числа.
- Строки - текстовые данные, заключённые в кавычки.
- Логические значения - true или false, для проверки условий.
- Списки, массивы, словари - структуры для хранения множества значений.
- Объекты - более сложные структуры.
★ Область видимости – часть кода, где переменная доступна (например, внутри функции или внутри всей программы). Переменные могут называться одинаково, и чтобы не конфликтовать друг с другом, определяется их рамки «влияния» - видимости.
Область видимости может быть:
- Глобальной - переменная доступна во всей программе, если объявляется вне всех функций;
- Локальной - переменная доступна только внутри определённой функции (блока кода) - за пределами она будет недоступна, и при использовании извне будет ошибка.

Практическое задание
Попробуйте составить два блока кода.
В блоке 1 напишите x=1.
В блоке 2 напишите y=2.
x и y будут переменными, которые находятся в разных областях видимости.
На техническом уровне переменная — это ссылка на область памяти компьютера, где хранится её значение. Когда объявляется переменная, ОС выделяет для неё место в памяти (не при написании кода, а при выполнении), и размер этой области зависит от типа данных. Когда изменяется значение переменной, старое значение удаляется, а новое записывается в ту же область памяти. Именно поэтому многие языки используют строгую типизацию - когда обязательно нужно указывать тип данных соответствующей переменной.
Переменные можно использовать для различных целей:
- фиксированное значение, когда значение не будет меняться;
- счётчик, который увеличивается или уменьшается на 1 (к примеру, количество попыток или запросов);
- флаг или признак, логическое значение (в основном с булевым значением);
- хранение нужного значения (к примеру, получив данные, мы для удобства записываем их в переменную);
- контейнер - структурированный набор данных (списки, массивы);
- временная переменная, нужная лишь на короткое время.
Прямое хранение значений (обычные переменные) подразумевает выделение места в памяти для конкретного типа данных, где будет храниться значение. К примеру, указав тип переменной a с типом int, мы выделим 4 байта и сохраним в них число 10. Если мы изменим значение переменной, старое значение будет переписано новым (допустим, a =20). Такой подход называется передачей по значению. Когда копируется переменная, создаётся её полная копия, и изменения в одной переменной не влияет на другую. Копирование выполняется приравниванием. Пример:
int b = a; // Создаётся копия значения a (b = 10)
a = 30; // Изменение a не влияет на b
// b всё ещё равно 10
Некоторые языки программирования (например, Python) работают через механизм ссылок, что означает, что переменная не хранит само значение, а указывает на объект в памяти. И изменение объекта через одну переменную автоматически отразится на другой, потому что они указывают на один и тот же объект в памяти. Это передача по ссылке.
a = [1, 2, 3]
b = a # b ссылается на тот же объект, что и a
b.append(4)
print(a) # Выведет: [1, 2, 3, 4]
Механизм ссылок более эффективно использует память, и если элементов миллионы, копирование ссылки занимает гораздо меньше ресурсов, чем создание полной копии объекта данных. Но изменения через одну переменную могут повлиять на другую, и бывает сложно отследить, какие переменные ссылаются на один объект.
Некоторые языки программирования комбинируют подходы, к примеру, в Java примитивные типы данных передаются по значению, а объекты - по ссылке. Но это уже особенности объектно-ориентированного программирования, о котором мы поговорим позже. Сейчас давайте разберёмся, что такое языки программирования.
★ Языки программирования – это формальные языки для написания инструкций, которые выполняет компьютер. Они бывают следующих категорий:
- Компилируемые – C, C++, Go – код преобразуется в машинный перед запуском;
- Интерпретируемые – Python, JavaScript – код выполняется построчно без предварительной компиляции;
- Универсальные – Java, C# - компилируются в промежуточный код, который выполняется виртуальной машиной.
В интерпретируемых языках код выполняется по строкам, по мере чтения, и поэтому ошибки находятся во время выполнения (runtime) — то есть только тогда, когда программа доходит до строки с проблемой, после начала работы программы.
Компилируемые языки отличаются в корне - перед запуском весь код сначала компилируется в машинный код или байт-код. Если есть синтаксическая ошибка, программа не скомпилируется, и запуск будет невозможен. Программа не запустится, пока вы не исправите ошибку.
IDE (например, PyCharm, VS Code, IntelliJ IDEA, Visual Studio) объединяют лучшее из обоих миров - подсвечивают синтаксические ошибки ещё до запуска, предлагают автодополнение, показывают подсказки по типам и параметрам, автоматически форматируют код, проверяют стиль и качество кода. Если мы напишем код в простом текстовом редакторе и сохраним файл, потом запустим - можем получить ошибку в терминале. А IDE сразу покажет нам ошибку ещё до запуска. В отличие от компилируемых языков, где ошибки находятся заранее, ещё до запуска программы, интерпретируемые языки могут работать частично — и упасть только тогда, когда дойдут до строки с ошибкой. Но современные IDE делают эту границу размытой: они проверяют код на лету, предупреждают о возможных проблемах и помогают писать качественный код даже новичкам.
Также языки делят на декларативный и императивный стиль.
Императивный (от латинского imperare - приказывать) подразумевает, что программа говорит «как именно делать», включая последовательное описание шагов, работу с состоянием, переменными, циклами, условиями. Это C, Java, Python, JavaScript, к примеру. Мы явно указываем как и что делать.
Декларативный же (от латинского declarare - объявлять) подразумевает, что программа говорит «что должно быть сделано», без внимания к деталям реализации, а акцент будет на результате, не процессе. Примеры - SQL, HTML, CSS, XSLT, регулярные выражения. Мы говорим, что нам нужно, а программа сама решит, как это сделать — это не наша забота.
★ Языки разметки описывают структуру и оформление данных, но не выполняют вычислений, к примеру, HTML – разметка веб-страниц.
★ Языки запросов используются для работы с базами данных, к примеру, SQL и GraphQL.
★ Комментарий – текст в коде, который не исполняется, а служит для пояснения. То, что внутри комментария – игнорируется.
Синтаксис комментария в разных языках:
| Язык | Однострочный комментарий | Многострочный комментарий |
|---|---|---|
| C / C++ / Java / C# / JavaScript / Swift / Go / Rust | // | /*…*/ |
| Python | # | '''или"""(для документации) |
| PHP | // или # | /*…*/ |
| Ruby | # | =begin ... =end |
| HTML | <!-- ... --> | <!-- ... --> |
| XML | <!-- ... --> | <!-- ... --> |
| JSX / React | {/* ... */} | {/* ... */} |
| YAML | # | не поддерживается |
| JSON | не поддерживается | не поддерживается |
| CSS | /*…*/ | /*…*/ |
| SQL | -- | /*…*/ |
| Bash / Shell | # | не поддерживается |
| Dockerfile | # | не поддерживается |