Числа из главы 01 и текст из главы 03 — это данные. Программа — принципиально другое: это тоже числа, но такие, которые процессор физически "исполняет" как команды к действию.
В самом низу лестницы абстракции — машинный код: последовательность чисел, каждое из которых процессор аппаратно распознаёт как конкретную операцию — сложить два числа, переместить данные из одной ячейки памяти в другую, перейти к выполнению другой части программы. Если ты уже знаком с логическими элементами (практический курс электроники) — процессор буквально и есть гигантская сеть таких элементов, физически "зашитых" так, чтобы определённая комбинация битов на входе всегда вызывала определённое действие схемы. Программа на уровне машинного кода — не метафора "компьютер выполняет команды", а буквальное описание того, что происходит физически.
Упрощённо процессор работает циклом из трёх шагов, повторяющимся миллиарды раз в секунду: выборка (взять следующую команду из памяти), декодирование (определить, какую именно операцию она означает) и исполнение (выполнить эту операцию — например, сложить два числа в определённых регистрах). После исполнения процессор переходит к следующей команде, и цикл повторяется.
Писать программы напрямую числами почти невозможно для человека — отсюда ассемблер
(assembly language): каждой машинной команде соответствует читаемое текстовое обозначение
(мнемоника) вроде MOV (переместить), ADD (сложить), JMP
(перейти). Это по-прежнему очень низкий уровень — программист вручную управляет конкретными
регистрами процессора и ячейками памяти, — но хотя бы читаемый человеком, а не голая
последовательность цифр.
Разные архитектуры процессоров (например, x86 у большинства настольных ПК и ARM у большинства смартфонов и части современных ноутбуков) физически понимают разные наборы машинных команд — то есть и разный ассемблер. Программа, написанная на ассемблере для одной архитектуры, не запустится на другой без полного переписывания под другой набор команд — серьёзное ограничение, с которого начинается мотивация для языков высокого уровня.
Языки высокого уровня (от классических Pascal и C до современного Python) позволяют описывать задачу в терминах, близких к человеческому мышлению или математической записи — "сложить два числа и сохранить результат в переменную x", а не явно указывать, в какой регистр процессора что положить. Одна строка кода высокого уровня разворачивается при трансляции в несколько (иногда десятки) машинных команд — переводческую работу берёт на себя отдельная программа (компилятор или интерпретатор, тема главы 05), а не сам программист вручную.
Условная строка кода x = 5 + 3 на языке высокого уровня примерно
разворачивается в такую последовательность на уровне машинных операций: загрузить число
5 в регистр, загрузить число 3 в другой регистр, выполнить операцию сложения над этими
регистрами, сохранить результат в ячейку памяти, отведённую под переменную x. Программист
пишет одну понятную строку — процессор в итоге выполняет несколько куда более
элементарных шагов.
Программа на языке высокого уровня, в отличие от ассемблера, обычно не привязана к конкретной архитектуре процессора напрямую — один и тот же исходный код (с поправками на особенности конкретной операционной системы) можно скомпилировать или интерпретировать под разные архитектуры отдельными инструментами трансляции, не переписывая сам код заново. Именно поэтому Python-скрипт, написанный на компьютере с одним типом процессора, обычно без изменений работает и на компьютере с другим типом процессора, лишь бы там был установлен подходящий интерпретатор Python.
Осталось не разобранным главное — как именно текст программы на языке высокого уровня физически превращается в машинный код, который реально выполнит процессор. Следующая глава — компиляция и интерпретация, два разных ответа на этот вопрос.
Книга бесплатна для чтения. Автор — Тариел Бежанидзе, компьютерный мастер в Батуми · tariel.ge · все бесплатные книги. Копировать текст целиком на другие сайты — только со ссылкой на источник.