Высоко и низко: чем языки «близко к железу» отличаются от человеческих
Одни языки говорят почти на языке процессора, другие — почти на человеческом. Эта «высота» определяет, насколько вам удобно и насколько вы близки к настоящему железу. Прокатимся по лестнице абстракций сверху донизу.
Можно объяснить компьютеру задачу почти на его родном языке цифр — а можно почти на человеческом. Между этими полюсами лежит вся лестница языков.
«Уровень» языка — это мера абстракции: насколько он близок к железу или, наоборот, к человеческому мышлению. Чем выше, тем удобнее писать, но тем меньше прямого контроля.
Самый низ: язык машины
В самом основании — машинный код, те самые числа, которые напрямую исполняет процессор. Писать на нём человеку практически невозможно. Чуть выше стоит ассемблер — это почти то же самое, но числа-команды заменены короткими мнемониками вроде MOV или ADD. Одна строка ассемблера — это, как правило, одна инструкция процессора. Здесь вы управляете каждым регистром и каждым байтом, но даже простое сложение чисел разворачивается в кучу строк.
Что значит «уровень»
Уровень языка — это про абстракцию: сколько деталей работы машины язык прячет от вас.
Низкоуровневые языки (ассемблер, отчасти C) держат вас близко к железу. Вы думаете в терминах памяти, адресов, байтов. Плюс — максимальный контроль и скорость. Минус — медленно, трудно и легко ошибиться. На них пишут то, что обязано выжимать всё из железа: операционные системы, драйверы, прошивки.
Высокоуровневые языки (Python, JavaScript, Java) прячут детали машины за удобными понятиями: списки, словари, объекты, готовые функции. Вы думаете о задаче, а не о байтах. Плюс — пишется быстро и читается легко. Минус — меньше прямого контроля над тем, что происходит внизу, и обычно чуть медленнее.
Одна задача на разной высоте
Покажем разницу на простом «сложить числа от 1 до 5». На высоком уровне это почти фраза на человеческом:
total = sum(range(1, 6))
print(total) # 15А вот идея того же на низком уровне (упрощённый псевдо-ассемблер): нужно вручную завести счётчик, аккумулятор, организовать цикл и сравнения.
загрузить 0 в сумму
загрузить 1 в счётчик
цикл:
прибавить счётчик к сумме
увеличить счётчик на 1
сравнить счётчик с 6
если меньше — перейти на цикл
напечатать суммуОдин и тот же результат, но сверху мы описываем что хотим, а снизу — как именно машина должна это сделать, шаг за шагом. И тут возникает законный вопрос: если внизу так неудобно, как высокоуровневый код вообще работает? Ответ в том, что между вашей удобной строкой и процессором стоит переводчик — компилятор или интерпретатор, — который сам разворачивает короткое человеческое описание в длинную низкоуровневую последовательность. Высокий уровень не отменяет низкий, а надстраивается над ним: вы пишете кратко, а машина под капотом всё равно получает свои подробные инструкции.
| Свойство | Низкоуровневые | Высокоуровневые |
| Близость к железу | Высокая | Низкая |
| Удобство для человека | Низкое | Высокое |
| Контроль над ресурсами | Полный | Ограниченный |
| Где применяют | ОС, драйверы, прошивки | Сайты, приложения, наука |
Высота — это не качество
Важно не путать «высокий уровень» с «лучше», а «низкий» с «хуже». Это просто разные этажи, выбираемые под задачу. Делаете веб-сервис — высокий уровень избавит от лишней возни. Пишете прошивку для крошечного чипа с парой килобайт памяти — без низкоуровневого контроля не обойтись. C вообще занимает любопытную середину: достаточно низкий, чтобы дать контроль над памятью, и достаточно структурный, чтобы на нём писали целые операционные системы. Чем больше языков с разных этажей вы знаете, тем точнее подбираете инструмент под конкретную работу.