Бывает ли, что программа компилируется с первого раза и без ошибок
Обновлено: 21.11.2024
Теперь, когда у меня был код для подвала, мне просто нужно было найти подвал и выяснить, как использовать код. За все время, проведенное на базе, я ни разу ничего не слышал о секретном складе, поэтому знал, что мне нужно кое-что расследовать. Я решил начать с того, что поднимусь на лифте на самый нижний этаж, а затем поищу какую-нибудь неприметную заднюю лестницу, которая приведет меня еще глубже.
Я спокойно подошел к лифту, нажал кнопку «вниз» и с облегчением увидел, что я один в коридоре. Когда двери открылись, моя удача продолжилась, и я отправился в одиночную поездку вниз. Я посмотрел на панель кнопок и, к моему удивлению, там была настоящая кнопка «Подвал». Может быть, мне не придется красться, чтобы найти вход!
Вы можете избежать деления на ноль, используя if:
Чтобы перейти к следующей части, я решил задачу:
Давным-давно, когда компьютерное программирование было в зачаточном состоянии, программисты писали программы на машинных кодах (языке, понятном процессору). Выглядело это так:
.
b8 21 0a 00 00
a3 0c 10 00 06
b8 6f 72 6c 64
.
Не самое легкое чтение, верно? При написании машинных кодов вы сообщаете процессору точный адрес области памяти, по которой следует читать и записывать данные. Каждая команда представлена числом, и поскольку это недостаточно сложно, команды различаются для разных процессоров.
Чтобы избежать этой сложности, умные люди начали изобретать абстракции и писать программы на более высоком уровне, чем исходный машинный код. Они разработали языки программирования, которые позволяют вам писать код, не беспокоясь о таких скрытых вещах, как управление памятью, работа с различным оборудованием и многих других вещах, о которых мы, программисты высокого уровня, можем даже не знать.
Современные разработчики программного обеспечения обычно работают на высоком уровне абстракции. Фактически, колоссальный процент программистов не знает полностью того, как работает их код вплоть до уровня процессора; они знают ровно столько, чтобы действовать на своем уровне. Благодаря языкам программирования высокого уровня программисты могут эффективно программировать, не разбираясь в каждой мелочи работы компьютера. Программист (фактически, ВЫ!) должны выяснить, насколько глубоко они хотят погрузиться в изучение того, как компьютеры переводят их код в функционирующие программы. По мере того, как ваше обучение здесь продолжается, мы обеспечим вас основами, которые помогут вам писать код и запускать свои программы.
На этом перестановки кода, которые происходят под капотом, не заканчиваются. После того, как IL-код сгенерирован, нашему процессору по-прежнему нечего читать (помните, что процессор может понимать только машинные коды). Поэтому кому-то или чему-то необходимо преобразовать код IL в машинные коды. Это нечто называется Common Language Runtime или CLR.
CLR — это программа, работающая на вашем компьютере и управляющая выполнением IL-кода. Проще говоря, он знает, как работать с IL и как выполнять программы, написанные на IL-коде. Он использует JIT-компилятор для преобразования кода IL в машинный код (иногда называемый «собственным» кодом). Компилятор JIT назван так потому, что он компилирует IL-код в тот момент, когда пользователь пытается его использовать; например, если дважды щелкнуть файл .exe.
Вкратце давайте взглянем на путь, который проходит ваш код от написания до выполнения:
Конечно, ваш код не всегда может работать безупречно. В вашей программе могут возникнуть ошибки двух типов: ошибки компилятора и ошибки времени выполнения.
Ошибки выполнения возникают, если проблема возникает во время работы программы. Обычно это серьезные ошибки, при которых среда выполнения останавливается, поскольку продолжение может повлиять на другие файлы или операционную систему. Возможные ошибки времени выполнения включают попытку программы разделить на ноль или записать в файл, доступный только для чтения. Ошибки во время выполнения намного опаснее, чем ошибки компилятора, поэтому разработчики стараются убедиться, что компилятор перехватывает как можно больше ошибок на этапе компиляции, прежде чем ваш код станет исполняемой программой.
Я читал и перечитывал терминологию, пока не почувствовал, что могу произнести ее наизусть. Выпив еще одну порцию слабого кофе, чтобы взбодриться, я вернулся к лифту и снова нажал кнопку «Подвал». Да, все еще ужасающая угроза смерти от лазера. Однако на этот раз я был довольно уверен в себе, и мне удалось стоять на месте, когда из потолка появилась большая лазерная пушка и направилась ко мне.Из панели напротив кнопок выскочил маленький тачскрин и дал мне следующие задачи:
Лазерная пушка втянулась обратно в потолочную панель. Ух ты! Это было не так сложно! Двери открылись, и я прошел по короткому коридору и начал спускаться по лестнице. Это должно привести меня туда, где я могу ввести секретный код!
Внезапно лестница начала вращаться. Все быстрее и быстрее.
Я пытался понять, правда это или нет, когда электронный голос нарушил мою концентрацию.
"Чтобы остановить это, докажите, что обладаете тайными знаниями."
Я сделал это, я решил все задачи! Мир, наконец, перестал вращаться, и то, что только что произошло, привело меня к нижней части лестницы лицом к стене. Там была установлена старая 10-значная клавиатура, и я ввел секретный код 145. Раздался скрежет, и вся стена повернулась, обнажив то, что все это время пряталось в подвале.
Мы в Codeasy считаем, что обучение должно быть веселым и увлекательным. Свяжитесь с нами, чтобы поделиться своими отзывами и идеями для совместной работы.
Исходный файл программы и выходные данные препроцессора и компилятора представляют собой текстовые файлы. Объектный файл и исполняемый файл являются двоичными файлами.
Когда вы пытаетесь запустить программу, операционная система создает новый процесс (с сопутствующими ресурсами), загружает исполняемый образ в память, а затем запускает процесс.
Есть несколько важных аргументов командной строки gcc, которые вы должны выработать в привычке. -Wall говорит gcc распечатать все предупреждения. У нас также есть -Wextra для дополнительных предупреждений. Это часто поможет вам обнаружить удивительное количество ошибок, которые не остановят компиляцию программы, но заставят ее работать неправильно. Другим аргументом, который вы всегда должны указывать, является -g , который указывает gcc выдавать специальную информацию, которую отладчик gdb может использовать для отладки вашей программы.
-Werror превращает предупреждения в ошибки, т. е. компилятор не будет создавать объектный код, если есть предупреждения. -Wfatal-errors приводит к остановке компилятора после первой ошибки. Я часто отключаю его.
Чтобы заставить gcc помочь вам написать стандарт C ANSI (важно, если вы хотите, чтобы ваш код работал под разными операционными системами и с разными компиляторами), вы также должны указать
Общая путаница
Как указано выше, результат каждого этапа компиляции можно просмотреть с помощью соответствующих аргументов компилятора. Необычно останавливать компиляцию после этапа предварительной обработки или после создания ассемблерного кода. Тем не менее, это часто, на самом деле это обычная процедура построения практических систем, когда компилятор останавливается после того, как он создаст объектные ( .o ) файлы, и снова использует его на отдельном этапе компоновки.
Начинающих это сбивает с толку, потому что, к сожалению, мы используем одну и ту же команду оболочки как для создания объектных файлов, так и для их связывания (например, gcc). Несмотря на то же имя команды, действия разные, и то, что требуется, отличается в двух случаях.
Предположим, что программе в файле с именем control-panel.c необходимо использовать пакет связанного списка и специализированный графический пакет, источник которых находится в linked-list.c и window-toolkit.c соответственно. Мы хотим создать программу панели управления, но как мы это сделаем?
- Преобразовать все исходные файлы в объектные файлы и
- Связать все объектные файлы вместе в исполняемый файл.
Важно понимать, что это только предоставляет компилятору информацию о типе, чтобы он знал, насколько большие значения данных возвращаются из внешних функций, сколько аргументов принимает функция и т. д. Этого достаточно для создания код объекта для control-panel.c . Чтобы получить объектный файл для control-panel.c, нам нужно указать компилятору не создавать исполняемый файл, а остановиться после фазы 3 компилятора с помощью ключа компилятора -c:
Если вы опустите -c , тогда gcc будет считать, что вы используете исполняемую программу, но когда он дойдет до фазы 3 компилятора, он обнаружит, что у него нет фактического определения, скажем, cons() . Вы получите сообщение об отсутствующей ссылке на cons() , и вам сообщат, что ld не удалось, т. е. программа не может быть слинкована.
Мы повторим эту процедуру для всех исходных файлов в системе, которую мы создаем, и тогда у нас будет набор файлов .o, которые ссылаются на значения и функции, к которым у них еще нет доступа.
Заключительный этап сборки происходит после создания всех объектных файлов. Исполняемой программе потребуются фактические определения внешних элементов для запуска, поэтому объектные файлы должны быть связаны друг с другом. То есть нам нужно выполнить 3 фазу процесса компиляции. На этот раз у нас уже есть все объектные файлы, но нам нужно разрешить ссылки между ними.Нам больше не нужны заголовочные файлы и исходные файлы C. Этот процесс сборки, который может быть очень сложным, можно автоматизировать. Стандартным инструментом Unix для решения этой проблемы является программа make. В Comp 40 мы используем для этого скрипты компиляции. Большинство крупных систем создаются с использованием IDE, которые содержат инструменты для управления сборками.
Забавный пример
[Я построил следующий пример на основе ошибки, выявленной Джеммой Стерн в тот самый день, когда она обсуждалась в классе. Слава ее острым глазам и удачному выбору времени!]
Во-первых, просто полюбуйтесь результатом cpp (эти результаты были получены при запуске gcc -E ). Все ссылки на darwin здесь, потому что я запускал gcc на Mac.
Теперь прокрутите вниз до строк чуть выше функции main() — она находится внизу. Теперь вы понимаете, почему в сообщениях об ошибках говорится, что они делают? (Возможно, вы не знали, что typedef — это «спецификатор класса хранения», но с этим знанием все должно стать на свои места.)
Вы видите, что такое пустое объявление? Видите ли вы определение данных без типа или класса хранения? Предполагая, что что-то является int ?
Советы по компиляции
- Можем ли мы создать программу только из UArray2? Что бы это значило?
- С чего начинается выполнение каждой программы C (и C++)?
Любой отдельный вызов gcc должен компилировать ссылку или, но не обе
Для каждого файла .h должен быть соответствующий файл .c или соответствующий файл .o (или оба).
Вы представляете файл .c и используете gcc -c .
Чтобы помочь компилятору найти файлы .h, используйте параметр -I.
Как узнать, в каких каталогах находятся нужные вам файлы .h? В каждой библиотеке по разному. Проконсультируйтесь с документацией, а еще лучше найдите знающего человека.
- Вы склеиваете набор файлов .o. Каждый из них содержит код перемещаемого объекта.
- Каждый файл .o либо упоминается в командной строке, либо загружается из библиотеки. Если в командной строке упоминается .o, вы всегда получаете его. Если файл .o находится в библиотеке, вы получите его только в том случае, если он предоставляет определение (например, printf ), которое используется функцией (например, main ) в другом файле .o, упомянутом в командной строке.
- Библиотека – это набор файлов .o.
- Вы получаете библиотеку, написав -l name, и компилятор ищет файл с именем lib name .a (или иногда lib name em> .so ).
- Чтобы указать компилятору, где искать библиотеки, используйте параметр -L во время компоновки.
Что может пойти не так:
Связать набор файлов .o без какой-либо функции main()
Связать набор файлов .o с помощью двух функций main()
Признаки того, что ваш скрипт компиляции неисправен:
Сочетание аргументов .c и .o
Опция -I, используемая на этапе связывания
Файл .c, переданный в качестве аргумента на этапе связывания
Опция -l или -L, используемая на этапе компиляции
Файл .o передается в качестве аргумента на этапе компиляции
Файл .h, передаваемый в качестве аргумента где угодно (продвинутый метод — не используйте его)
Хотя императивное программирование часто используется, декларативный подход оказался полезным перед лицом требований к сложным, .
На первый взгляд, разница между микроприложениями и микросервисами просто связана с проблемами внешнего интерфейса и серверной части. Но .
IDP могут предоставить продуктивную и безопасную среду для групп разработчиков. Рассмотрите все за и против, чтобы увидеть, является ли внутренний .
Разрастание UX-дизайна имитирует разрастание городов как в стремлении к росту, так и в потенциально опасных подводных камнях. Вот несколько вещей .
Запах кода может стать причиной плохого кодирования в угольной шахте. А плохое кодирование — это признак того, что требуется рефакторинг. Давайте .
Чтобы успешно передать Agile-разработку на аутсорсинг, организации должны уделять первостепенное внимание общению и обеспечивать достаточную адаптацию.
Преодолейте сбои AWS, научившись создавать многорегиональную архитектуру, обеспечивающую отказоустойчивость в случае аварии.
Чтобы добиться высокой доступности и отказоустойчивости в AWS, ИТ-администраторы должны сначала понять различия между двумя моделями.
Amazon ECS и EKS похожи, но их различий достаточно, чтобы выделить их для пользователей AWS. Узнайте, что лучше всего подходит для вашего .
Продавец средств аутентификации Okta стал последним технологическим гигантом, который стал жертвой плодовитой команды Lapsus$ благодаря ключевым деталям .
Корпоративный бизнес финского поставщика средств защиты начинается сам по себе как новый бренд под названием WithSecure, в то время как F-Secure будет .
Предупреждение президента Джо Байдена о возможных атаках России на важнейшую инфраструктуру США является последним призывом к действию для .
Считаете, что готовы к сертификационному экзамену AWS Certified Solutions Architect? Проверьте свои знания, ответив на эти 12 вопросов и.
Amazon заявила, что ее система мониторинга микроавтобусов предназначена исключительно для обеспечения безопасности водителей. Но многие отраслевые эксперты обеспокоены этим.
Amazon хотела бы укрепить свое глобальное присутствие, но гигант электронной коммерции сегодня сталкивается с препятствиями и проблемами, которых у него не было.
Компьютеры не понимают человеческие языки. Фактически, на самом низком уровне компьютеры понимают только последовательности чисел, представляющие операционные коды (коды операций для краткости). С другой стороны, людям было бы очень сложно писать программы в терминах кодов операций. Поэтому были изобретены языки программирования, чтобы облегчить людям написание компьютерных программ.
Языки программирования предназначены для чтения и понимания людьми. Программа (исходный код) должна быть переведена на машинный язык, чтобы компьютер мог выполнить программу (поскольку компьютер понимает только машинный язык). То, как происходит этот перевод, зависит от того, является ли язык программирования компилируемым или интерпретируемым языком.
Компилируемые языки (например, C, C++)
Следующее иллюстрирует процесс программирования для скомпилированного языка программирования.
Компилятор берет программный код (исходный код) и преобразует исходный код в модуль машинного языка (называемый объектным файлом). Другая специализированная программа, называемая компоновщиком, объединяет этот объектный файл с другими ранее скомпилированными объектными файлами (в частности, модулями времени выполнения) для создания исполняемого файла. Этот процесс изображен на схеме ниже. Нажмите Начальная сборка, чтобы увидеть анимацию создания исполняемого файла. Щелкните Запустить исполняемый файл, чтобы имитировать запуск уже созданного исполняемого файла. Нажмите «Перестроить», чтобы имитировать перестроение исполняемого файла.
Исходная сборка Запустить исполняемый файл Rebuild
Таким образом, для скомпилированного языка преобразование исходного кода в исполняемый код происходит до запуска программы. Этот процесс сильно отличается от того, что происходит в интерпретируемом языке программирования.
Это несколько упрощено, так как многие современные программы, созданные с использованием скомпилированных языков, используют динамически подключаемые библиотеки или общие библиотеки. Поэтому для запуска исполняемого файла могут потребоваться эти динамические связанные библиотеки (Windows) или общие библиотеки (Linux, Unix).
Интерпретируемые языки программирования (например, Python, Perl)
Для интерпретируемого языка этот процесс отличается. Вместо перевода исходного кода на машинный язык до создания исполняемого файла интерпретатор преобразует исходный код на машинный язык во время работы программы. Это показано ниже:
В интерпретируемых языках используется специальная программа, называемая интерпретатором, которая преобразует исходный код, объединяется с библиотеками времени выполнения и выполняет полученные машинные инструкции во время выполнения. В отличие от скомпилированного языка, здесь нет предварительно скомпилированной программы для запуска. Процесс преобразования и объединение с библиотеками времени выполнения происходит каждый раз, когда запускается программа на интерпретируемом языке. Вот почему программы, написанные на скомпилированных языках, работают быстрее, чем аналогичные программы, написанные на интерпретируемых языках. Нажмите Start, чтобы запустить симуляцию интерпретируемой программы. Нажмите «Перезапустить», если хотите снова запустить симуляцию.
Каждый раз, когда запускается интерпретируемая программа, интерпретатор должен преобразовывать исходный код в машинный код, а также извлекать библиотеки времени выполнения. Этот процесс преобразования заставляет программу работать медленнее, чем сопоставимая программа, написанная на скомпилированном языке.
Поскольку интерпретатор выполняет преобразование исходного языка в машинный во время выполнения программы, интерпретируемые языки обычно приводят к тому, что программы выполняются медленнее, чем скомпилированные программы. Но взамен часто получается, что интерпретируемые языки часто не зависят от платформы, поскольку для каждой операционной системы можно использовать разные интерпретаторы.
А теперь кое-что другое. Java
Язык программирования Java не вписывается ни в модели компилируемого языка, ни в модели интерпретируемого языка. Это показано на рисунке ниже.
Компилятор Java (javac) преобразует исходный код в байт-код. Байт-код — это своего рода средний машинный язык. Этот файл байт-кода (файл .class) можно запустить в любой операционной системе с помощью интерпретатора Java (java) для этой платформы. Интерпретатор называется виртуальной машиной. Таким образом, Java является примером языка программирования для виртуальных машин.
Языки виртуальных машин были созданы как компромисс между компилируемыми и интерпретируемыми языками. В идеальных условиях программы на языке виртуальной машины работают ближе по скорости к программам на скомпилированном языке, но обладают независимостью от платформы программ на интерпретируемом языке.
Способ, которым языки программирования виртуальных машин получают часть скорости компилируемых языков, заключается в том, что исходный код пропускается через компилятор для создания байт-кода. Это преобразование происходит до запуска программы. Способ, которым языки виртуальных машин достигают своей переносимости (независимости от платформы), заключается в наличии разных интерпретаторов для каждой поддерживаемой операционной системы. Этот интерпретатор связывает правильные библиотеки времени выполнения для каждой операционной системы. Скомпилированный байт-код представляет собой средний машинный язык, который будет работать без изменений с любым интерпретатором виртуальной машины для этого языка. Этот процесс проиллюстрирован далее. У нас есть компилятор, который преобразует исходный код в байт-код. Это можно смоделировать, нажав кнопку Compile. После создания байт-кода этот же байт-код можно использовать без каких-либо изменений в любой операционной системе, имеющей интерпретатор виртуальной машины для языка программирования. Обратите внимание, что каждый из интерпретаторов виртуальных машин имеет разный код библиотеки времени выполнения, поскольку каждая операционная система имеет разные библиотеки времени выполнения. Вот как язык виртуальной машины решает проблемы зависимости от платформы. Нажмите «Запустить Windows», «Запустить Mac OSX» или «Запустить Linux», чтобы имитировать запуск программы в любой из этих операционных систем.
Читайте также: