Большинство новичков в программировании, при написании очередной программы на уровне «Hello world», просто нажимают кнопку Run и даже не задумывается о том, что происходит с их кодом в момент компиляции. А зря.
Итак, вы на канале Дад’а и в этой статье вы получите частичку Computer Science. Мы разберём цикл работы компилятора. При этом, не погружаясь в самые недра. Погружаться будем уже в другой статье 🙂
Подписывайтесь на канал, ставьте лайк и мы начинаем!
Для чего мне это нужно?
Если у вас сейчас появился такой вопрос, то вот ответ на него:
Не понимая основ программирования, как всё работает, вы не сможете писать по-настоящему оптимизированный код. И дело тут не в правилах вроде «Тщательно выбирайте имена для переменных».
Надеюсь, вы меня понимаете. Если всё Ok, давайте наконец начнём!
Компиляция — это перевод кода на языке высокого уровня в машинную форму представления. Иными словами, это перевод с одного языка на другой, более понятный компьютеру.
#1. Этапы трансляции программы в машинный код. Стандарты | Язык C для начинающих
Шаг первый — Препроцессор
В момент нажатия кнопки Run , вы отправляете свой код в компилятор. Всё начинается с препроцессора:
Исходный текст программы обрабатывается препроцессором (Будем считать, что это программа, которая тем или иным образом работает с текстом). Он ищет в тексте (в вашем коде) директивы, которые начинаются с символа «решётка». К сожалению, дзен превращает этот символ вот в это: «#»
На всякий случай, этот символ выглядит так:
Итак, препроцессор ищет в вашем коде директивы, затем выполняет их.
Директивы позволяют вставлять в программу текст (код) из других файлов, исключать из процесса компиляции фрагменты кода, выполнять замену одних фрагментов другими и т.п.
Один из самых распространённых примеров :
Ссылается на заголовочный файл stdio.h, в процессе компиляции библиотека stdio будет включена в наш проект.
Шаг второй. Анализ.
Обработанный текст передаётся назад в компилятор, который выполняет синтаксический и лексический анализ полученного текста.
Лексический анализ
На этом этапе сканер (лексический анализатор) последовательно просматривает поступающий в него поток символов и выделяет допустимые лексемы , это могут быть имена / ключевые слова, знаки операций, разделители и т.п. Их границы определяются по разделителям, пробельным символам и другим лексемам.
Синтаксический анализ
После лексического анализа парсер (синтаксический анализатор), на основе грамматики языка, распознает построенные из лексем выражения и операторы, выявляет синтаксические ошибки.
Семантический анализ
Целью этого вида анализа является выявление разного рода смысловых ошибок. Например, повторное описание переменной.
Шаг три. Почти финал.
Вам было тяжело? Надеюсь, что нет. Мы скоро закончим.
Итак, если ошибок после всех предыдущих этапов нет — > начинается генерация кода. При этом, конкретный вид генерируемого кода зависит от того, приложение какого типа создаётся.
С чего началось программирование? Первые программы до появления кода
Для обычного Windows приложения строится объектник (объектный модуль) — заготовка исполняемой программы в машинном коде.
Финал?
Далее судьба этого приложения тоже зависит от типа приложения.
Для Windows приложения компоновщик (линкер) формирует исполняемый .exe файл, подключая к объектному модулю другие такие же модули, в том числе, содержащие элементы стандартных библиотек, которые вы используете в своём проекте (например, stdio).
Если программа состоит из нескольких файлов, они компилируются по-отдельности и объединяются на этапе компоновки. После всего этого мы имеем готовый .exe файл, который можно запускать.
Заключение
В заключение хочу сказать, что изучать компьютерную науку (CS) — очень важно. В данный момент на рынке очень много разработчиков без действительно-сильной теоретической базы. В том числе и я. Именно по этой причине я решил углубиться в CS.
Ставьте лайки и подписывайтесь на канал. Это не только мотивирует меня, но и способствует популяризации канала.
Чем больше подписчиков и лайков я получаю, тем больше у меня желание выдавать вам качественный и полезный контент, поэтому:
Ставьте лайки и подписывайтесь на канал! 🙂
Спасибо за внимание, с вами был Дад.
Пишите в комментариях, что вы думаете о новом «логотипе» и названии канала, нравится ли вам?
Также пишите ваше мнение о данной статье, считаете ли вы её полезной. Любые ваши отклики улучшают качество контента на этом канале!
Источник: dzen.ru
Как компьютер понимает программу написанную на языке высокого уровня
Основное различие между интерпретатором компилятора и ассемблером заключается в том, чтоompiler переводит всю языковую программу высокого уровня на машинный язык за раз, в то время как переводчик переводит программу высокого уровня на машинный язык построчно, а ассемблер конвертирует программу на ассемблере в машинный язык.
Компьютерная программа — это набор инструкций для компьютера для выполнения определенной задачи. Большинство программ написаны на языках высокого уровня или на ассемблере. Эти программы легко читаются и понимаются программистами, но не понятны компьютеру. Компьютер понимает только машинный язык. Он состоит из двоичного кода, который состоит из единиц и нулей.
Следовательно, программа высокого уровня или программа сборки должна быть переведена на машинный язык, чтобы компьютер мог понять инструкции. Компилятор, интерпретатор и ассемблер — это переводчики, которые преобразуют программы на высоком уровне или на языке ассемблера в машинный язык.
Ключевые области покрыты
1. Что такое компилятор
— определение, функциональность
2. Что такое переводчик
— определение, функциональность
3. Что такое Ассемблер
— определение, функциональность
4. Разница между интерпретатором компилятора и ассемблером
— Сравнение основных различий
Основные условия
Компилятор, Интерпретатор, Ассемблер
Что такое компилятор
Компилятор — это языковой переводчик, который преобразует программы высокого уровня в машинно-понятные машинные коды. В этом процессе компилятор одновременно конвертирует всю программу в машинный код. Если есть какие-либо синтаксические или семантические ошибки, компилятор укажет их. Он проверяет всю программу и отображает все ошибки. Невозможно выполнить программу без исправления этих ошибок.
Рисунок 1: Компилятор
Языки программирования, такие как C, C ++, используют компилятор для преобразования языка. Время выполнения ниже в этих языках. Поэтому они считаются быстрыми языками.
Что такое переводчик
Переводчик также является переводчиком языка, который преобразует программы высокого уровня в машинные коды. В отличие от компиляторов, интерпретаторы преобразуют исходный код в машинный код построчно. Поскольку он проверяет построчно, время сканирования меньше. Но общее время выполнения выше.
Интерпретатор отображает ошибку одновременно. Программист должен исправить эту ошибку, чтобы интерпретировать следующую строку. Языки программирования, такие как Python, Ruby, PHP, Perl, являются некоторыми примерами языков на основе интерпретатора.
Что такое Ассемблер
В дополнение к языкам высокого уровня и машинному языку существует еще один язык, называемый языком ассемблера. Язык ассемблера находится между языками высокого уровня и машинным языком. Это ближе к машинному языку, чем к языкам высокого уровня. Это также называется языком низкого уровня.
Этот язык не легко читается и понятен программисту, как язык программирования высокого уровня. Ассемблер работает как переводчик в преобразовании программы на языке ассемблера в машинный код.
Разница между интерпретатором компилятора и ассемблером
Определение
Компилятор — это программа, которая преобразует программы, написанные на языке высокого уровня, в машинный язык. Интерпретатор — это программа, которая переводит языковую программу высокого уровня на машинный язык, а ассемблер — это программа, которая преобразует программы, написанные на ассемблере, в машинный язык.
функциональность
Компилятор преобразует всю языковую программу высокого уровня в машинный язык одновременно. Интерпретатор переводит языковую программу высокого уровня в машинный язык построчно. Напротив, ассемблер конвертирует программу на ассемблере в машинный язык.
язык
Такие языки, как C, C ++, используют компиляторы для преобразования кода. Такие языки, как Ruby, Perl, Python, PHP используют интерпретатор, а язык ассемблера использует ассемблер.
Компилятор, Интерпретатор и Ассемблер являются языковыми переводчиками. Разница между интерпретатором компилятора и ассемблером заключается в том, что компилятор одновременно конвертирует все программы высокого уровня в машинный язык, а интерпретатор переводит программы высокого уровня в машинный язык построчно, а ассемблер конвертирует программы на ассемблере в машинный язык.
Ссылка:
1. «Что такое Ассемблер, Интерпретатор и Компилятор? (На английском языке) », зона изучения экзамена, 9 сентября 2017 года,
Источник: ru.strephonsays.com
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;
Как и программы на ассемблере, программы, написанные на языках высокого уровня, прежде чем их можно будет запустить, должны быть переведены в формат, понятный компьютеру. Это можно сделать двумя основными способами: компиляция и интерпретация.
Компилятор – это программа, которая читает исходный код и создает автономную исполняемую программу, которую затем можно запустить. После того, как ваш код был преобразован в исполняемый файл, вам не нужен компилятор для запуска программы. Вначале компиляторы были примитивными и создавали медленный, неоптимизированный код. Однако с годами компиляторы стали очень хороши в создании быстрого, оптимизированного кода и в некоторых случаях могут делать его лучше, чем люди, пишущие на ассемблере!
Ниже показано упрощенное представление процесса компиляции:
Поскольку программы на C++ обычно компилируются, мы вскоре рассмотрим компиляторы более подробно.
Интерпретатор – это программа, которая непосредственно выполняет инструкции в исходном коде, не требуя их предварительной компиляции в исполняемый файл. Интерпретаторы, как правило, более гибки, чем компиляторы, но менее эффективны при запуске программ, потому что при запуске программы каждый раз должен выполняться процесс интерпретации. Это означает, что при каждом запуске программы требуется интерпретатор.
Ниже показано упрощенное представление процесса интерпретации:
Дополнительная информация
Ни один из подходов не имеет перед другим явного преимущества – если бы один подход всегда был лучше, велика вероятность, что мы стали бы использовать его повсюду!
В целом, компиляторы имеют следующие преимущества:
- Поскольку они могут видеть весь исходный код заранее, при генерации кода они могут выполнять ряд анализов и оптимизаций, благодаря чему окончательная версия кода выполняется быстрее, чем простая интерпретация каждой строки по отдельности.
- Компиляторы часто могут генерировать низкоуровневый код, который выполняет эквивалент высокоуровневых идей, таких как «динамическое связывание» или «наследование» с точки зрения поиска в памяти внутри таблиц. Это означает, что итоговые программы должны помнить меньше информации об исходном коде, что снижает использование памяти сгенерированной программой.
- Скомпилированный код обычно быстрее, чем интерпретируемый код, потому, что выполняемые инструкции обычно предназначены только для самой программы, а не для самой программы плюс накладные расходы интерпретатора.
В целом, у компиляторов есть следующие недостатки:
- Некоторые языковые функции, такие как динамическая типизация, сложно эффективно скомпилировать, потому, что компилятор не может предсказать, что произойдет, пока программа не будет запущена. Это означает, что компилятор может генерировать не очень хороший код.
- Компиляторы обычно имеют длительное время «запуска» из-за затрат на выполнение всего анализа, который они делают. Это означает, что в таких окружениях, как веб-браузеры, где важно быстро загружать код, компиляторы могут работать медленнее, потому что они оптимизируют короткий код, который не будет запускаться много раз.
В целом, у интерпретаторов есть следующие преимущества:
- Поскольку они могут читать код в том виде, в каком он написан, и им не нужно выполнять дорогостоящие операции для генерации или оптимизации кода, они, как правило, запускаются быстрее, чем компиляторы.
- Поскольку интерпретаторы могут видеть, что делает программа во время ее выполнения, интерпретаторы могут использовать ряд динамических оптимизаций, которые компиляторы могут не видеть.
В целом, у интерпретаторов есть следующие недостатки:
- Интерпретаторы обычно используют больше памяти, чем компиляторы, потому, что интерпретатору необходимо хранить больше информации о программе, доступной во время выполнения.
- Интерпретаторы обычно тратят некоторое время процессора внутри кода для интерпретатора, что может замедлить выполнение программы.
Поскольку у интерпретаторов и компиляторов есть взаимодополняющие сильные и слабые стороны, в средах выполнения языка становится всё более обычным явлением сочетание элементов обоих методов. Хорошим примером этого является 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 разных инструкций.
В-третьих, программы можно компилировать (или интерпретировать) для многих различных систем, и вам не нужно изменять программу для работы на разных процессорах (вы просто перекомпилируете ее для конкретного процессора). В качестве примера:
В портируемости есть два общих исключения. Во-первых, многие операционные системы, такие как Microsoft Windows, содержат специфичные для платформы возможности, которые вы можете использовать в своем коде. Это может значительно упростить написание программы для конкретной операционной системы, но за счет усложнения портируемости. В данной серии статей мы избегаем кода, специфичного для какой-либо платформы.
Некоторые компиляторы также поддерживают специфичные для компилятора расширения – если вы их используете, ваши программы без изменений не смогут компилироваться другими компиляторами, которые не поддерживают те же расширения. Мы поговорим об этом позже, когда вы установите компилятор.
Правила, рекомендации и предупреждения
По мере изучения этих руководств мы будем выделять важные моменты в следующих трех категориях:
Правило
Правила – это инструкции, которые вы должны выполнять в соответствии с требованиями языка. Несоблюдение правил обычно приводит к тому, что ваша программа не работает.
Лучшая практика
Лучшие практики – это то, что вам следует делать, потому что такой способ обычно считается стандартным или настоятельно рекомендуется. То есть либо так поступают все (а если вы поступите иначе, то вы будете делать не то, что ожидают), либо это лучше других альтернатив.
Предупреждение
Предупреждения – это то, чего делать не следует, поскольку они обычно приводят к неожиданным результатам.
Источник: radioprog.ru