Перейти к содержимому

Какой язык программирования работает напрямую с процессором

  • автор:

Ассемблер

Ассемблер (англ. «Assembler») — это низкоуровневый язык программирования, который представляет собой промежуточное звено между машинным кодом и высокоуровневыми языками программирования. Он используется для написания программ, которые управляют компьютером или другими устройствами на более низком уровне, непосредственно взаимодействуя с аппаратным обеспечением. Код, написанный на этом языке, обычно сохраняется с помощью расширения ASM.

Программы на ассемблере пишутся в виде набора мнемонических инструкций, каждая из которых соответствует определенной команде процессора. Эти инструкции затем транслируются (ассемблируются) в машинный код — набор двоичных чисел, которые понимает центральный процессор и выполняет соответствующие операции.

Так выглядит язык ассемблер

Начинают изучение программирования обычно с вывода на экран строки «Hello, world!». В языке программирования Python для этого достаточно одной команды:

print("Hello, World!")

Просто, понятно и красиво. Однако, существует язык программирования, в котором для достижения того же результата необходимо написать более обширный кусок кода на ассемблере:

.MODEL SMALL .STACK 100h .DATA HelloMessage DB 'Hello, World!',13,10,'$' .CODE START: mov ax,@data mov ds,ax mov ah,9 mov dx,OFFSET HelloMessage int 21h mov ah,4ch int 21h END START

Несмотря на видимое отличие в сложности между Python и ассемблером, важно понимать, что каждая его команда выполняет всего лишь одну операцию, в то время как одна команда Python вызывает несколько операций процессора при выполнении. Оба языка имеют свои преимущества и применяются в различных сферах программирования.

Кратко про процессоры и машинный язык

Для более полного понимания языка ассемблера начнем с основ работы процессора и того, на каком языке можно общаться с ним.

язык ассемблер низкоуровневый язык программирования

Процессор представляет собой электронное устройство, которое, несмотря на свою маленькую размерность сегодня (раньше процессоры занимали целые залы), не обладает способностью понимать слова или цифры. Его реакция основана исключительно на двух уровнях напряжения: высокий уровень соответствует «1», а низкий уровень — «0». Таким образом, каждая команда процессора представляет собой последовательность нулей и единиц: «1» — это импульс, а «0» — его отсутствие.

Для взаимодействия с процессором используется машинный язык, который состоит из инструкций, записанных в двоичной форме. Каждая инструкция определяет одну простую машинную операцию: арифметические действия над числами, логические операции (побитовые), ввод-вывод и так далее.

Например, в архитектуре Intel 8088 инструкция 0000001111000011B представляет операцию сложения двух чисел, в то время как 0010101111000011B выполняет вычитание.

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

Поэтому был разработан язык ассемблера, в котором операции обозначаются буквами и сокращениями английских слов, отражающих суть команды. Например, команда mov ax, 6 означает: «переместить число 6 в регистр AX».

Основы ассемблера

Ниже приведена таблица с примерами машинного кода, соответствующими инструкциями на ассемблере и их описаниями:

Компьютер на лампах, предшественник эпохи ассемблера

Проблема была решена, когда ЭВМ научились хранить программы в памяти. В 1950 году была разработана первая программа-транслятор, которая переводила программы, написанные на понятном человеку языке, в машинный код. Эта программа была названа программой-сборщиком, а язык программирования получил название «ассемблера» (от английского слова «assembler» — сборщик). Впервые этот термин стал использовать английский учёный Морис Уилкс (Maurice Wilkes).

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

Как устроен язык ассемблер

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

Код ассемблера

Команды ассемблера состоят из кодов операций и операндов. Операнды представляют собой адреса, из которых процессор берет данные для вычислений и в которые он помещает результат. Эти адреса могут быть ячейками оперативной памяти или регистрами — внутренней памятью процессора. Процессор работает с регистрами гораздо быстрее, чем с оперативной памятью.

Операции в языке ассемблера имеют мнемоническую форму, что делает их удобными для запоминания:

  • ADD — сложение (от англ. addition);
  • SUB — вычитание (от англ. subtraction);
  • MUL — умножение (от англ. multiplication) и так далее.

Регистры и ячейки памяти получают символические имена, например:

  • EAX , EBX , AX , AH — имена для регистров;
  • mem1 — имя для ячейки памяти.

Пример команды сложения чисел из регистров AX и BX :

И вот команда вычитания чисел из регистров AX и BX :

В языке ассемблера также присутствуют директивы — команды управления компилятором, то есть программой-ассемблером.

Некоторые из них:

  • INCLUDE — открыть файл и начать его компиляцию;
  • EXIT — прекратить компиляцию файла;
  • DEF — назначить регистру символическое имя и так далее.

Не думайте, что ассемблер — это просто набор инструкций процессора с удобной записью для программиста. Это полноценный язык программирования, на котором можно реализовать циклы, условные операторы, процедуры и функции.

Вот пример ассемблерного кода, который выводит на экран цифры от 1 до 10:

section .text global _start _start: mov ecx,10 mov eax, '1' label1: mov [num], eax mov eax, 4 mov ebx, 1 push ecx mov ecx, num mov edx, 1 int 0x80 mov eax, [num] sub eax, '0' inc eax add eax, '0' pop ecx loop label1 mov eax,1 int 0x80 section .bss num resb 1

Вакансия программист на ассемблере с зарплатой и требованиями

Предлагаемая заработная плата обычно соответствует стандартам сферы информационных технологий и может варьироваться от 80 до 300 тыс. руб. в зависимости от уровня квалификации и опыта кандидата.

Стоит ли начинать изучение программирования с языка ассемблера

Нет, такой подход не рекомендуется. На это есть несколько причин:

  • ассемблер существенно отличается от языков высокого уровня, поэтому переход с него на другой язык может быть сложным и запутанным процессом;
  • опыт, полученный при изучении ассемблера, не будет особенно полезным при работе с другими языками. При изучении высокоуровневых языков после него, вам придется начинать все с нуля;
  • ассемблер — очень детальный язык программирования. Все рутинные действия, которые обрабатываются автоматически трансляторами в других языках, приходится описывать здесь вручную. Это может быстро надоесть и стать утомительным.

Поэтому, даже если ваша профессия связана с ассемблером, рекомендуется начать изучение программирования с языка высокого уровня. После этого изучение ассемблера будет проходить более легко и эффективно.

Статьи по теме:

Уровни языков программирования: какие бывают и чем они отличаются

Уровни языков программирования: какие бывают и чем они отличаются

В мире насчитывают около 9000 языков программирования. Среди них есть низкоуровневые и высокоуровневые. В чем их отличие, и почему низкий уровень не хуже высокого — рассказываем в статье.

Освойте профессию «Веб-разработчик»
Веб-разработчик с нуля

Веб-разработчик — мастер на все руки. Он создает программы и приложения для любых сфер и компаний: от небольшой кофейни до международных банков. Станьте специалистом, который создает востребованный продукт

картинка - 2023-03-14T190017.151

Профессия / 12 месяцев
Веб-разработчик с нуля
Создавайте нужные любому бизнесу сервисы
3 658 ₽/мес 6 650 ₽/мес

vsrat_8 (3)

Что такое язык программирования

  • алфавит — символы, которые используются для передачи команд;
  • синтаксис — правила написания запросов;
  • семантику — смысловое значение команд.

С помощью языка программирования можно изменять данные на компьютере, производить вычисления, создавать новые приложения. При этом все языки взаимодействуют с компьютером на разных уровнях — низких или высоких.

Низкоуровневые языки

«Мозг» компьютера — это процессор. Именно он отдает указания и контролирует работу устройства. Процессор распознает команды только в двоичном коде — написанные при помощи нулей и единиц (100110). Цифры помогают машине понять, что именно нужно сделать — вывести на экран изображение, положить свитшот в корзину или запустить любимую игру.

У низкоуровневых языков нет встроенных функций. Они общаются с конкретным процессором, поэтому код одного компьютера не может быть использован на другом. У каждого производителя процессоров свой собственный набор инструкций.

Самые распространенные низкоуровневые языки — это машинный код и язык ассемблера.

Машинный код

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

Код передается в загрузчик программ напрямую, минуя «посредников», поэтому требует меньше памяти и отличается высокой производительностью.

Язык ассемблера

Чтобы немного упростить управление «железом», популярные команды преобразуют в более понятные директивы. Они похожи на сокращенные английские слова. Например, можно написать MOV вместо команды 1011, чтобы переместить данные в соседний регистр.

Пример низкоуровневого языка ассемблера

Язык ассемблера — это первая надстройка, которая упрощает машинный код. Но при этом все еще позволяет писать высокопроизводительные программы и приложения.

Сегодня язык ассемблера используют для создания программного обеспечения или драйверов. Чтобы компьютер понял этот язык, все команды пропускаются через компилятор или интерпретатор и переводят обратно в двоичный код.

Какие языки программирования учить и зачем

Языки высокого уровня не хуже и не лучше языков низкого уровня. Как правило, они используются для разных задач. Выбирать язык программирования следует исходя из поставленной цели. Например, для мобильной разработки используют C++, Java, JavaScript. Для создания веб-сайтов подойдет Python, JavaScript, Ruby, PHP. Для драйверов можно использовать низкоуровневый язык ассемблера.

Языки низкого уровня и некоторые языки высокого уровня, например, С++, будут сложными для новичков. Поэтому тем, кто только начинает кодить, рекомендуют выбирать высокоуровневые языки с упрощенным синтаксисом — Python, JavaScript. Даже не имея глубоких познаний в информатике, с их помощью можно создать интересный проект и внедрить много небанальных решений.

Что такое язык ассемблера и стоит ли его изучать

Ассемблер используют разработчики микроконтроллеров и драйверов — те, кто работает с железом.

Анастасия Хамидулина
Автор статьи
5 мая 2023 в 9:01

Про Python, Java, C пишут в блогах онлайн-школ, на хабре и ви-си. Эти языки — лидеры по рейтингу TIOBE, их часто выбирают новички, чтобы изучать как первый язык программирования. Но знания менее популярных языков программирования тоже востребованы. В статье рассматриваем язык ассемблера: о нём расскажет Алексей Каньков, старший backend-разработчик Revizto.

Что такое ассемблер

Язык ассемблера (Assembly, или ASM) — это язык программирования, который используют, чтобы написать программы для аппаратных платформ или архитектур.

В отличие от языков программирования высокого уровня, например Python или Java, язык ассемблера обеспечивает прямое представление инструкций машинного кода. Поэтому язык ассемблера называют языком низкого уровня: он ближе к двоичному коду, который понимает компьютер.

Если больший интерес вызывают языки высокого уровня, приходите на курс Skypro «Java-разработчик». За 11 месяцев научитесь работать с самыми популярными инструментами языка программирования Java и станете востребованным специалистом с дипломом о профессиональной переподготовке. Сможете устроиться на работу уже во время обучения.

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

Когда и как был создан

Язык программирования ассемблер существует с первых дней вычислительной техники. Его развитие можно проследить до первых электронных компьютеров, построенных в 1940-х и 1950-х.

Один из первых примеров языка ассемблера — язык, используемый для программирования компьютера Manchester Mark 1. Его разработала группа исследователей под руководством Фредерика Уильямса и Тома Килберна из Манчестерского университета в Англии. Manchester Mark 1 был одним из первых компьютеров, использующих архитектуру с хранимой программой. Его язык применяли для написания программ, которые хранились в его памяти.

Компьютерное оборудование развивалось — совершенствовались и языки ассемблера. Добавили новые инструкции и функции для более сложных операций и новых аппаратных возможностей. Сегодня язык ассемблера по-прежнему используют в специализированных областях. Например, в программировании встроенных систем и низкоуровневом системном программировании.

Python-разработчик: новая работа через 9 месяцев
Получится, даже если у вас нет опыта в IT

Где используют язык ассемблера

Сейчас используют множество различных языков ассемблера, каждый из которых предназначен для конкретной аппаратной платформы или архитектуры. Примеры:

  • x86 для ПК на базе Intel;
  • ARM для мобильных устройств и встроенных систем;
  • MIPS для некоторых встроенных систем и академического использования.

Ассемблер нужен в областях, где требуется низкоуровневое системное программирование или аппаратное управление:

�� Разработка операционной системы. Язык ассемблера используют при разработке ОС и драйверов устройств, для которых нужен прямой доступ к аппаратным компонентам.

�� Программирование встроенных систем. Ассемблер применяют для разработки микроконтроллеров и других небольших устройств с ограниченной вычислительной мощностью.

�� Разработка игр. Язык нужен, чтобы оптимизировать критически важные для производительности участки кода. Примеры игр, в которых использовали ассемблер: TIS-100, RollerCoaster Tycoon, Shenzhen I/O, Human Resource Machine. Правда, эти игры скорее для программистов: в них разрабатывают имитацию кода.

�� Обратный инжиниринг. Язык ассемблера часто используют для дизассемблирования и анализа двоичного кода.

�� Разработка вредоносных программ. Хакеры создают на ассемблере вирусы.

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

Если хочется работать на них — приходите на курс «Python-разработчик». За 10 месяцев научитесь создавать логику программ, баз данных и разрабатывать приложения. Выполните несколько проектных работ: например, сделаете планировщик задач с авторизацией через соцсети и возможностью управлять карточками. Получите навыки, которые востребованы во многих IT-компаниях.

Как устроен язык ассемблера

�� Синтаксис

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

``` MOV AX, 1 ; move the value 1 into the AX register ADD AX, BX ; add the value in the BX register to the AX register ```

В этом примере MOV и ADD — это мнемоники для инструкций «переместить» и «добавить». AX и BX — это операнды. Они относятся к регистрам, в которых хранятся данные.

Синтаксис языка ассемблера точный и структурированный, потому что предназначен для работы с машинным кодом. Но это затрудняет чтение и написание кода для программистов, привыкших к языкам более высокого уровня.

Если хотите изучать более универсальные и популярные языки, начните с Java и Python. В онлайн-университете Skypro есть такие курсы: учим с нуля, делаем упор на практику. Научитесь разрабатывать приложения, сайты, социальные сети, игры, доски объявлений. В конце — диплом и помощь с работой. Не просто подбираем вакансии, а устраиваем на новую работу — или возвращаем деньги за обучение.

�� Директивы

В языке ассемблера директивы — это специальные инструкции. Они используются для предоставления дополнительной информации ассемблеру или компоновщику, а не выполняются как часть программы. Директивы обычно обозначают специальным символом, например точкой или решеткой.

�� `SECTION`: эта директива нужна для определения разделов программы, которые используют для группировки связанного кода и данных вместе.

�� `ORG`: чтобы установить исходный или начальный адрес программы или раздела.

�� `EQU`: чтобы определить константы или символы, которые используют во всей программе.

�� `DB`, `DW`, `DD`: для определения значений данных байтов, слов или двойных слов в памяти.

�� `ALIGN`: для выравнивания ячейки памяти следующей инструкции или значения данных с указанной границей.

�� `EXTERN`, `GLOBAL`: чтобы указать, определяется ли символ внешне или глобально. Эту информацию использует компоновщик для разрешения ссылок на символы в разных объектных файлах.

�� `INCLUDE`: для включения файла кода на языке ассемблера в текущую программу.

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

�� Команды

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

�� Команды перемещения данных. Перемещают данные между регистрами или ячейками памяти: MOV, PUSH и POP.

�� Арифметические команды. Выполняют арифметические операции с данными в регистрах или ячейках памяти: ADD, SUB и MUL.

�� Логические команды. Выполняют логические операции с данными в регистрах или ячейках памяти: AND, OR и XOR.

�� Команды ветвления. Управляют путем перехода к другому разделу кода: JMP, JZ и JE.

�� Команды стека. Управляют стеком — областью памяти для хранения данных — и управляющей информацией во время вызовов функций и возвратов: PUSH и POP.

��Системные вызовы. Позволяют программам на ассемблере взаимодействовать с операционной системой или другими системными функциями, такими как INT, которые запускают программное прерывание.

�� Ассемблерный код

Примеры фрагментов кода ассемблера для архитектуры x86:

``` section .data msg db 'Hello, world!', 0 section .text global _start _start: mov eax, 4 ; System call for write mov ebx, 1 ; File descriptor for stdout mov ecx, msg ; Address of message to print mov edx, 13 ; Length of message int 0x80 ; Call kernel mov eax, 1 ; System call for exit xor ebx, ebx ; Exit code 0 int 0x80 ; Call kernel ```

Эта программа определяет строку сообщения в разделе .data, а затем использует инструкцию mov для настройки параметров системного вызова. Выводит на экран сообщения с помощью системного вызова записи. Затем программа завершается с кодом выхода 0.

Сумма двух чисел:

``` section .data a dw 5 b dw 7 section .text global _start _start: mov ax, [a] ; Load first number into AX add ax, [b] ; Add second number to AX mov cx, ax ; Save result in CX mov eax, 1 ; System call for exit xor ebx, ebx ; Exit code 0 int 0x80 ; Call kernel ```

Эта программа определяет два значения в разделе .data, а потом использует инструкции mov и add для вычисления суммы двух чисел и сохранения результата в регистре cx. Затем программа завершается с кодом выхода 0.

Программа для вычисления последовательности Фибоначчи:

``` section .data n dw 10 section .bss fib resw 10 section .text global _start _start: mov eax, [n] mov ebx, 0 mov ecx, 1 mov edx, 2 mov [fib+ebx], ecx .loop: cmp edx, eax jge .done add ecx, [fib+ebx] mov [fib+edx], ecx mov ebx, edx inc edx jmp .loop .done: mov eax, 1 xor ebx, ebx int 0x80 ```

Эта программа использует цикл для вычисления первых n чисел в последовательности Фибоначчи и сохранения их в массиве, в разделе .bss. Затем программа завершается с кодом выхода 0.

Достоинства и недостатки ассемблера

Преимущества

Эффективность: программы на языке ассемблера можно оптимизировать для конкретной архитектуры — это делает их эффективными и быстрыми.

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

Небольшой размер кода: программы на языке ассемблера обычно меньше программ на языках более высокого уровня. Это важно в определенных встроенных системах или других средах с ограниченным объемом памяти.

Переносимость: язык используют для написания кода, который можно скомпилировать для работы на разных платформах с соответствующими модификациями.

Отладка: язык ассемблера полезен для отладки низкоуровневых проблем в программах или оборудовании.

Недостатки

Сложность: язык ассемблера гораздо сложнее написать и понять, чем языки более высокого уровня. Программирование на ассемблере утомительно и занимает много времени: разработчики должны писать код даже для самых простых операций.

Сначала лучше изучать более популярные, универсальные и простые языки. Например, на курсе Skypro «Python-разработчик» изучите Python — его применяют на разных платформах и широко используют в разработке интернет-приложений, программного обеспечения и машинном обучении.

Ограниченная абстракция: в языке нет многих абстракций и высокоуровневых конструкций — это затрудняет написание сложных программ.

Сопровождение: программы на ассемблере трудно поддерживать, потому что изменения в аппаратном или программном обеспечении требуют значительных обновлений кода.

Отладка: это еще и минус, потому что отладка кода на языке ассемблера сложная — проблемы низкого уровня трудно диагностировать и исправить.

Стоит ли изучать язык ассемблера

Это зависит от ваших целей и интересов. Если хотите писать высокопроизводительный код для конкретной аппаратной платформы или устройства, ассемблер полезен. Еще знания пригодятся для отладки низкоуровневых проблем в программах или оборудовании.

Но учитывайте, что язык ассемблера требует глубокого понимания компьютерной архитектуры и наборов инструкций. Учить его сложнее, чем языки более высокого уровня. Особенно если нет опыта в области компьютерных наук, вы еще не знакомы с архитектурой компьютера и концепциями низкоуровневого программирования.

Востребованы ли программисты на ассемблере сегодня

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

Этот язык программирования используют только для максимально эффективной разработки, потому что команды работают с процессором или контроллером напрямую. То есть код ассемблера будет максимально быстро исполняться и четко работать.

Изучать его стоит, если вы планируете программировать микросхемы или писать эффективные программы для процессоров. Потому что писать программы на ассемблере трудоемко, а разрабатывать приложения с интерфейсами для пользователей бессмысленно.

На 13 марта 2023-го на хедхантере 50 075 вакансий программистов, а вакансий с упоминанием Assembler всего 244 по России — меньше 0,5%. Но с учетом тренда на импортозамещение спрос на таких программистов может вырасти.

Юрий Гизатуллин, руководитель и сооснователь digital-агентства TIQUM, сооснователь RB7.ru

Примеры вакансий на хедхантере с упоминанием ассемблера: зарплаты от 100 000 ₽ до 500 000 ₽

Главное: что такое ассемблер

  • Ассемблер — это язык программирования низкого уровня. Он нужен для программирования микроконтроллеров или написания программ, которые работают с процессорами напрямую. Еще его используют для анализа двоичного кода, создания вирусов, оптимизации важных для производительности участков кода при разработке игр.
  • Преимущества языка ассемблера: низкоуровневый контроль над аппаратными ресурсами, небольшой размер кода. Код ассемблера можно скомпилировать для работы на разных платформах с соответствующими модификациями. Язык ассемблера полезен для отладки низкоуровневых проблем в программах или оборудовании. Программы на языке ассемблера можно оптимизировать для конкретной архитектуры.
  • Недостатки: в языке нет многих абстракций и высокоуровневых конструкций, его сложно изучать, а программы на ассемблере трудно поддерживать.

0.2 – Введение в языки программирования

Современные компьютеры невероятно быстры и постоянно становятся еще быстрее. Однако они также имеют некоторые существенные ограничения: они изначально понимают только ограниченный набор команд, и им нужно точно сказать, что делать.

Компьютерная программа (также обычно называемая приложением) – это набор инструкций, которые компьютер может выполнить для выполнения некоторой задачи. Процесс создания программы называется программированием. Программисты обычно создают программы, создавая исходный код (обычно сокращено говорят просто код), который представляет собой список команд, введенных в один или несколько текстовых файлов.

Набор физических компонентов компьютера, из которых состоит компьютер, и которые выполняют программы, называется аппаратным обеспечением. Когда компьютерная программа загружается в память и аппаратное обеспечение последовательно выполняет каждую инструкцию, это называется запуском или выполнением программы.

Машинный язык

Центральный процессор (CPU) компьютера не понимает язык C++. Ограниченный набор инструкций, которые CPU может понимать напрямую, называется машинным кодом (или машинным языком, или набором инструкций).

Вот пример инструкции машинного языка: 10110000 01100001 .

Когда были изобретены первые компьютеры, программистам приходилось писать программы непосредственно на машинном коде, что было очень трудно и отнимало много времени.

Как организованы эти инструкции, выходит за рамки данного введения, но интересно отметить две вещи.

  • Во-первых, каждая инструкция состоит из последовательности нулей и единиц. Каждый отдельный 0 или 1 называется битом (от «binary digit», или «двоичная цифра»). Количество битов, составляющих одну команду, может различаться – например, некоторые процессоры обрабатывают инструкции, которые всегда имеют длину 32 бита, тогда как некоторые другие процессоры (например, семейство x86, которое вы, вероятно, используете) имеют инструкции, которые могут быть переменной длины.
  • Во-вторых, каждый набор битов интерпретируется центральным процессором в команду для выполнения очень конкретной работы, например, сравнения этих двух чисел или помещения этого числа в эту ячейку памяти. Однако из-за того, что разные CPU имеют разные наборы инструкций, инструкции, написанные для одного типа процессоров, не могут использоваться процессором, который не использует такой же набор команд. Это означало, что программы, как правило, нельзя было портировать (переносить, использовать без серьезной доработки) между различными типами систем, и их приходилось переписывать заново.

Язык ассемблера

Поскольку людям трудно читать и понимать машинный код, был изобретен ассемблер. В языке ассемблера каждая инструкция идентифицируется коротким сокращением (а не набором битов), и могут использоваться имена и другие числа.

Вот та же инструкция, что и выше, но на языке ассемблера: mov al, 061h .

Это сделало разработку намного проще для чтения и записи, чем машинный код. Однако CPU не понимает ассемблер напрямую. Вместо этого программа на ассемблере, прежде чем она сможет быть выполнена компьютером, должна быть переведена на машинный код. Это делается с помощью программы, называемой ассемблером. Программы, написанные на языках ассемблера, обычно бывают очень быстрыми, и ассемблер всё еще используется сегодня, когда очень важна скорость выполнения программы.

Однако у ассемблера всё же есть недостатки. Во-первых, языки ассемблера по-прежнему требуют использования множества инструкций даже для простых задач. Хотя отдельные инструкции в некоторой степени удобочитаемы, понимание того, что делает вся программа, может быть сложной задачей (это немного похоже на попытку понять предложение, рассматривая каждую букву по отдельности). Во-вторых, язык ассемблера по-прежнему не очень портируем – программа, написанная на ассемблере для одного процессора, скорее всего, не будет работать на аппаратном обеспечении, использующем другой набор инструкций, и ее придется переписывать или сильно модифицировать.

Высокоуровневые языки

Чтобы решить проблемы удобочитаемости и переносимости, были разработаны новые языки программирования, такие как C, C++, Pascal (и более поздние языки, такие как Java, Javascript и Perl). Эти языки называются языками высокого уровня, поскольку они разработаны, чтобы позволить программисту писать программы, не беспокоясь о том, на каком компьютере будет выполняться программа.

Вот всё та же инструкция, что и выше, на C/C++: a = 97;

Как и программы на ассемблере, программы, написанные на языках высокого уровня, прежде чем их можно будет запустить, должны быть переведены в формат, понятный компьютеру. Это можно сделать двумя основными способами: компиляция и интерпретация.

Компилятор – это программа, которая читает исходный код и создает автономную исполняемую программу, которую затем можно запустить. После того, как ваш код был преобразован в исполняемый файл, вам не нужен компилятор для запуска программы. Вначале компиляторы были примитивными и создавали медленный, неоптимизированный код. Однако с годами компиляторы стали очень хороши в создании быстрого, оптимизированного кода и в некоторых случаях могут делать его лучше, чем люди, пишущие на ассемблере!

Ниже показано упрощенное представление процесса компиляции:

Рисунок 1 – Пример сборки приложения

Поскольку программы на C++ обычно компилируются, мы вскоре рассмотрим компиляторы более подробно.

Интерпретатор – это программа, которая непосредственно выполняет инструкции в исходном коде, не требуя их предварительной компиляции в исполняемый файл. Интерпретаторы, как правило, более гибки, чем компиляторы, но менее эффективны при запуске программ, потому что при запуске программы каждый раз должен выполняться процесс интерпретации. Это означает, что при каждом запуске программы требуется интерпретатор.

Ниже показано упрощенное представление процесса интерпретации:

Рисунок 2 – Пример интерпретации

Дополнительная информация

Ни один из подходов не имеет перед другим явного преимущества – если бы один подход всегда был лучше, велика вероятность, что мы стали бы использовать его повсюду!

В целом, компиляторы имеют следующие преимущества:

  1. Поскольку они могут видеть весь исходный код заранее, при генерации кода они могут выполнять ряд анализов и оптимизаций, благодаря чему окончательная версия кода выполняется быстрее, чем простая интерпретация каждой строки по отдельности.
  2. Компиляторы часто могут генерировать низкоуровневый код, который выполняет эквивалент высокоуровневых идей, таких как «динамическое связывание» или «наследование» с точки зрения поиска в памяти внутри таблиц. Это означает, что итоговые программы должны помнить меньше информации об исходном коде, что снижает использование памяти сгенерированной программой.
  3. Скомпилированный код обычно быстрее, чем интерпретируемый код, потому, что выполняемые инструкции обычно предназначены только для самой программы, а не для самой программы плюс накладные расходы интерпретатора.

В целом, у компиляторов есть следующие недостатки:

  1. Некоторые языковые функции, такие как динамическая типизация, сложно эффективно скомпилировать, потому, что компилятор не может предсказать, что произойдет, пока программа не будет запущена. Это означает, что компилятор может генерировать не очень хороший код.
  2. Компиляторы обычно имеют длительное время «запуска» из-за затрат на выполнение всего анализа, который они делают. Это означает, что в таких окружениях, как веб-браузеры, где важно быстро загружать код, компиляторы могут работать медленнее, потому что они оптимизируют короткий код, который не будет запускаться много раз.

В целом, у интерпретаторов есть следующие преимущества:

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

В целом, у интерпретаторов есть следующие недостатки:

  1. Интерпретаторы обычно используют больше памяти, чем компиляторы, потому, что интерпретатору необходимо хранить больше информации о программе, доступной во время выполнения.
  2. Интерпретаторы обычно тратят некоторое время процессора внутри кода для интерпретатора, что может замедлить выполнение программы.

Поскольку у интерпретаторов и компиляторов есть взаимодополняющие сильные и слабые стороны, в средах выполнения языка становится всё более обычным явлением сочетание элементов обоих методов. Хорошим примером этого является JVM – сам код Java компилируется и изначально интерпретируется. Затем JVM может найти код, который выполняется много-много раз, и скомпилировать его непосредственно в машинный код, что означает, что «горячий» код получает преимущества компиляции, а «холодный» – нет. JVM также может выполнять ряд динамических оптимизаций, таких как встроенное кэширование, для повышения производительности способами, которые обычно не выполняются компиляторами.

Многие современные реализации JavaScript используют аналогичные приемы. Большая часть кода JavaScript короткая и не так уж много чего делает, поэтому обычно начинают с интерпретатора. Однако если станет ясно, что код запускается неоднократно, многие JS-движки скомпилируют код (или, по крайней мере, скомпилируют его отдельные части) и оптимизируют его, используя стандартные методы. В конечном итоге код работает быстро при запуске (полезно для быстрой загрузки веб-страниц), а также быстрее работает и при дальнейшем выполнении.

И последняя деталь: языки не компилируются и не интерпретируются. Обычно код C компилируется, но доступны и интерпретаторы C, которые упрощают отладку или визуализацию выполняемого кода (они часто используются при вводном изучении программирования – или, по крайней мере, они использовались раньше). JavaScript считался интерпретируемым языком, пока некоторые JS-движки не начали его компилировать. Некоторые реализации Python являются чисто интерпретаторами, но вы можете найти компиляторы Python, которые генерируют нативный код. Некоторые языки легче компилировать или интерпретировать, чем другие, но ничто не мешает вам создать компилятор или интерпретатор для любого конкретного языка программирования. Теоретический результат, называемый проекциями Футамуры, показывает, что всё, что можно интерпретировать, можно и скомпилировать.

Большинство языков можно компилировать или интерпретировать, однако традиционно языки, такие как C, C++ и Pascal, компилируются, тогда как «скриптовые» языки, такие как Perl и Javascript, обычно интерпретируются. В некоторых языках, например в Java, используется сочетание этих двух методов.

Высокоуровневые языки обладают многими необходимыми свойствами.

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

Во-вторых, высокоуровневые языки требуют меньшего количества инструкций, чем низкоуровневые языки, для выполнения одной и той же задачи, что делает программы более краткими и легкими для понимания. В C++ вы можете сделать что-то вроде a = b * 2 + 5; в одну строку. На ассемблере для этого потребуется 5 или 6 разных инструкций.

В-третьих, программы можно компилировать (или интерпретировать) для многих различных систем, и вам не нужно изменять программу для работы на разных процессорах (вы просто перекомпилируете ее для конкретного процессора). В качестве примера:

Рисунок 3 – Пример портируемости приложения

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

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

Правила, рекомендации и предупреждения

По мере изучения этих руководств мы будем выделять важные моменты в следующих трех категориях:

Правило

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

Лучшая практика

Лучшие практики – это то, что вам следует делать, потому что такой способ обычно считается стандартным или настоятельно рекомендуется. То есть либо так поступают все (а если вы поступите иначе, то вы будете делать не то, что ожидают), либо это лучше других альтернатив.

Предупреждение

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

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *