Скомпилированный файл, что это такое

Обновлено: 04.07.2024

Исходный файл программы и выходные данные препроцессора и компилятора представляют собой текстовые файлы. Объектный файл и исполняемый файл являются двоичными файлами.

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

Есть несколько важных аргументов командной строки gcc, которые вы должны выработать в привычке. -Wall говорит gcc распечатать все предупреждения. У нас также есть -Wextra для дополнительных предупреждений. Это часто поможет вам обнаружить удивительное количество ошибок, которые не остановят компиляцию программы, но заставят ее работать неправильно. Другим аргументом, который вы всегда должны указывать, является -g , который указывает gcc выдавать специальную информацию, которую отладчик gdb может использовать для отладки вашей программы.

-Werror превращает предупреждения в ошибки, т. е. компилятор не будет создавать объектный код, если есть предупреждения. -Wfatal-errors приводит к остановке компилятора после первой ошибки. Я часто отключаю его.

Чтобы заставить gcc помочь вам написать стандарт C ANSI (важно, если вы хотите, чтобы ваш код работал под разными операционными системами и с разными компиляторами), вы также должны указать

Общая путаница

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

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

Предположим, что программе в файле с именем control-panel.c необходимо использовать пакет связанного списка и специализированный графический пакет, источник которых находится в linked-list.c и window-toolkit.c соответственно. Мы хотим создать программу панели управления, но как мы это сделаем?

  1. Преобразовать все исходные файлы в объектные файлы и
  2. Связать все объектные файлы вместе в исполняемый файл.

Важно понимать, что это только предоставляет компилятору информацию о типе, чтобы он знал, насколько большие значения данных возвращаются из внешних функций, сколько аргументов принимает функция и т. д. Этого достаточно для создания код объекта для 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 ?

Советы по компиляции

  1. Можем ли мы создать программу только из UArray2?Что бы это значило?
  2. С чего начинается выполнение каждой программы 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 .so ).
  • Чтобы указать компилятору, где искать библиотеки, используйте параметр -L во время компоновки.

Что может пойти не так:

Связать набор файлов .o без какой-либо функции main()

Связать набор файлов .o с помощью двух функций main()

Признаки того, что ваш скрипт компиляции неисправен:

Сочетание аргументов .c и .o

Опция -I, используемая на этапе связывания

Файл .c, переданный в качестве аргумента на этапе связывания

Опция -l или -L, используемая на этапе компиляции

Файл .o передается в качестве аргумента на этапе компиляции

Файл .h, передаваемый в качестве аргумента где угодно (продвинутый метод — не используйте его)

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

Пакет
Подпись

компилировать-файл входной-файл &key выходной-файл подробный print external-format load => output-truename , warnings-p , failure-p

Аргументы

Указатель пути.

Указатель пути.

Обобщенное логическое значение.

Обобщенное логическое значение.

Спецификация внешнего формата.

Обобщенное логическое значение.

Значения

Путь или ноль .

Обобщенное логическое значение.

Обобщенное логическое значение.

Описание

compile-file вызывает компилятор для преобразования исходного файла Lisp в форму, которая загружается и работает быстрее. Скомпилированная функция обычно работает более чем в десять раз быстрее, чем интерпретируемая (при условии, что она не тратит большую часть своей работы на вызов уже скомпилированных функций). Исходный файл с расширением .lisp или .lsp компилируется для создания файла с расширением .*x*fasl (фактическое расширение зависит от процессора хост-компьютера). Последующее использование load загружает скомпилированную версию (в формате LispWorks FASL или Fast Load) вместо исходного кода.

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

output-file указывает расположение выходного файла. Этот аргумент полезен, если вы используете нестандартное расширение для двоичных файлов. Если вы используете нестандартные расширения для двоичных файлов, вы должны сообщить об этом LispWorks, поместив строку расширения файла в переменную sys::*binary-file-types* . Если вы этого не сделаете, LispWorks будет считать эти файлы текстовыми, а не скомпилированными. См. пример ниже.

verbose управляет печатью сообщений с описанием компилируемого файла, текущими настройками оптимизации и другой информацией. Если verbose равно nil, сообщений нет. Если verbose равен 0, печатается только сообщение «Compiling file.». Для всех других истинных значений verbose также печатаются сообщения о:

<УЛ>
  • параметры оптимизации компилятора перед обработкой файла и
  • множественные совпадения, когда во входном файле не указан тип имени пути, и
  • любая очистка (сборка мусора), которая происходит во время компиляции.
  • Значением по умолчанию является значение *compile-verbose* , которое по умолчанию равно t .

    print управляет печатью информации о компиляции. Он может иметь следующие значения. Если print равно nil, информация не печатается. Если print — неположительное число, то печатаются только предупреждения. Если print является положительным числом, не превышающим 1, или если print является любым нечисловым объектом, то печатаемая информация состоит из всех предупреждающих сообщений и одной строки информации для каждой скомпилированной функции. Если print является числом больше 1, то печатается полная информация.Значением print по умолчанию является значение *compile-print* , которое имеет значение по умолчанию 1.

    внешний формат интерпретируется как открытый. Значение по умолчанию: :default .

    Если load имеет значение true, файл загружается после компиляции.

    output-truename — это истинное имя выходного файла или nil, если его невозможно создать.

    warnings-p имеет значение nil, если во время компиляции не было обнаружено условий типа ошибки или предупреждения. В противном случае warnings-p — это список, содержащий условия.

    failure-p имеет значение nil, если компилятор не обнаружил условий ошибки или предупреждения типа (кроме style-warning ), и t в противном случае.

    Примеры
    Примечания

    См. declare список объявлений, которые изменяют поведение компилятора.

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

    По умолчанию форма пропускается, если во время компиляции возникает ошибка. Если вам нужно отладить ошибку во время компиляции с помощью compile-file , установите *compiler-break-on-error*.

    Во время компиляции файла foo.lisp (например, в Mac OS X) используется временный выходной файл с именем t_foo.nfasl, чтобы неудачная компиляция не перезаписывала существующий foo.nfasl .

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

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

    Существует два типа файлов машинного языка.

    Исполняемый файл — это законченная программа на машинном языке. Вы можете запустить его в команде, просто введя его имя (или путь) в качестве команды. Если вы хотите запустить исполняемый файл, который находится в текущем каталоге, напишите перед ним ./. Например, запускает исполняемый файл frog в текущем каталоге.

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

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

    Подборка

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

    Компиляция программы на C++

    Используйте g++ для компиляции программы на C++. Команда переводит prog.cpp с машинного языка C++. По умолчанию он записывает файл с именем a.out. Вы можете изменить это, используя параметры командной строки, написанные между g++ и именем программы. Варианты включают следующее.

    -o файл

    Записать программу на машинном языке в файл file. Если вы не укажете имя, программа на машинном языке будет называться a.out.

    Включить все часто используемые предупреждения. Это настоятельно рекомендуется.

    Без этой опции g++ создает исполняемый файл. С этой опцией он создает объектный файл. Если имя выходного файла не указано (опция -o), имя объектного файла для файла prog.cpp будет prog.o.

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

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

    Например, команда создает исполняемый файл prog из файла C++ prog.cpp с включенными общими предупреждениями и записанной в него информацией отладчика. Команда создает объектный файл prog.o, готовый к связыванию с другими объектными файлами. Вы не можете запустить prog.o сам по себе.

    Запуск программы

    Имя файла исполняемой программы также является командой для запуска этой программы. Итак, если вы скомпилируете градин.cpp с помощью команды, тогда g++ запишет файл градина. Команда запускает программу.

    Остановка мошеннической программы

    Если ваша программа входит в бесконечный цикл, вам нужно будет его остановить. Введите контроль-C. Обычно это останавливает его.

    Если по какой-то причине Control-C не работает, есть более сложные способы остановить это. Возможно, вам придется запустить другой терминал.Команда показывает все процессы, которые у вас запущены в данный момент. Найдите ту, которая является вашей программой. У него есть номер процесса. Если n — это номер процесса, команда обычно останавливает его. Если это не так, то всегда остановит это.

    Упражнения

    В чем разница между исполняемым файлом и объектным файлом? Ответить

    Что делает компилятор? Ответить

    Как можно скомпилировать assn1.cpp и создать исполняемый файл с именем assn1 с часто используемым запросом предупреждения? Ответить

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

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

    Пакет
    Подпись

    компилировать-файл входной-файл &key выходной-файл подробный print external-format load => output-truename , warnings-p , failure-p

    Аргументы

    Указатель пути.

    Указатель пути.

    Обобщенное логическое значение.

    Обобщенное логическое значение.

    Спецификация внешнего формата.

    Обобщенное логическое значение.

    Значения

    Путь или ноль .

    Обобщенное логическое значение.

    Обобщенное логическое значение.

    Описание

    compile-file вызывает компилятор для преобразования исходного файла Lisp в форму, которая загружается и работает быстрее. Скомпилированная функция обычно работает более чем в десять раз быстрее, чем интерпретируемая (при условии, что она не тратит большую часть своей работы на вызов уже скомпилированных функций). Исходный файл с расширением .lisp или .lsp компилируется для создания файла с расширением .*x*fasl (фактическое расширение зависит от процессора хост-компьютера). Последующее использование load загружает скомпилированную версию (в формате LispWorks FASL или Fast Load) вместо исходного кода.

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

    output-file указывает расположение выходного файла. Этот аргумент полезен, если вы используете нестандартное расширение для двоичных файлов. Если вы используете нестандартные расширения для двоичных файлов, вы должны сообщить об этом LispWorks, поместив строку расширения файла в переменную sys::*binary-file-types* . Если вы этого не сделаете, LispWorks будет считать эти файлы текстовыми, а не скомпилированными. См. пример ниже.

    verbose управляет печатью сообщений с описанием компилируемого файла, текущими настройками оптимизации и другой информацией. Если verbose равно nil, сообщений нет. Если verbose равен 0, печатается только сообщение «Compiling file.». Для всех других истинных значений verbose также печатаются сообщения о:

    <УЛ>
  • параметры оптимизации компилятора перед обработкой файла и
  • множественные совпадения, когда во входном файле не указан тип имени пути, и
  • любая очистка (сборка мусора), которая происходит во время компиляции.
  • Значением по умолчанию является значение *compile-verbose* , которое по умолчанию равно t .

    print управляет печатью информации о компиляции. Он может иметь следующие значения. Если print равно nil, информация не печатается. Если print — неположительное число, то печатаются только предупреждения. Если print является положительным числом, не превышающим 1, или если print является любым нечисловым объектом, то печатаемая информация состоит из всех предупреждающих сообщений и одной строки информации для каждой скомпилированной функции. Если print является числом больше 1, то печатается полная информация. Значением print по умолчанию является значение *compile-print* , которое имеет значение по умолчанию 1.

    внешний формат интерпретируется как открытый. Значение по умолчанию: :default .

    Если load имеет значение true, файл загружается после компиляции.

    output-truename — это истинное имя выходного файла или nil, если его невозможно создать.

    warnings-p имеет значение nil, если во время компиляции не было обнаружено условий типа ошибки или предупреждения. В противном случае warnings-p — это список, содержащий условия.

    failure-p имеет значение nil, если компилятор не обнаружил условий ошибки или предупреждения типа (кроме style-warning ), и t в противном случае.

    Примеры
    Примечания

    См. declare список объявлений, которые изменяют поведение компилятора.

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

    По умолчанию форма пропускается, если во время компиляции возникает ошибка. Если вам нужно отладить ошибку во время компиляции с помощью compile-file , установите *compiler-break-on-error*.

    Во время компиляции файла foo.lisp (например, в Mac OS X) используется временный выходной файл с именем t_foo.nfasl, чтобы неудачная компиляция не перезаписывала существующий foo.nfasl .

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

    Читайте также: