Зачем сначала компилировать в объектный файл? - PullRequest
31 голосов
/ 12 марта 2011

В прошлом году я начал программировать на Фортране, работая в исследовательском университете.Большая часть моего предыдущего опыта работы с веб-языками, такими как PHP или старый ASP, так что я новичок в составлении операторов .

У меня есть два разных кода, которые я изменяю.

Один имеет явное выражение , создающее файлы .o из модулей (например, gfortran -c filea.f90) перед созданием исполняемого файла.

Другой создает исполняемый файл напрямую (иногда создаются файлы .mod, но нет файлов .o, например, gfortran -o исполняемый файлa.f90 fileb.f90 mainfile.f90).

  • Есть ли причина (кроме, может быть, Makefiles), что один метод предпочтительнее другого?

Ответы [ 5 ]

29 голосов
/ 12 марта 2011

Сначала компиляция в объектные файлы называется отдельной компиляцией.Есть много преимуществ и несколько недостатков.

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

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

Недостатки:

  • Есть некоторые оптимизации (такие как оптимизация функций), которые компилятор не может выполнить, и компоновщик не заботится;однако многие компиляторы теперь включают возможность выполнять «оптимизацию времени соединения», что в значительной степени устраняет этот недостаток.Но это все еще проблема для системных библиотек и сторонних библиотек, особенно для разделяемых библиотек (невозможно оптимизировать отдельные части компонента, которые могут изменяться при каждом запуске, однако другие методы, такие как JIT-компиляция, могут смягчить это).
  • в некоторых языках программист должен предоставить какой-то заголовок для использования другими, которые будут связываться с этим объектом.Например, в C вы должны предоставить .h файлы для ваших объектных файлов.Но в любом случае это хорошая практика.
  • в языках с текстовым включением, таких как C или C ++, если вы изменяете прототип функции, вы должны изменить его в двух местах.Один раз в заголовочном файле, один раз в файле реализации.
14 голосов
/ 12 марта 2011

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

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

5 голосов
/ 12 марта 2011

.o файл - это объектный файл.Это промежуточное представление конечной программы.

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

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

Например.

Если у вас есть основная программа, и она вызывает подпрограмму A.(Это ложный фортран, я не касался десятилетий, так что работайте со мной здесь.)

PROGRAM MAIN
INTEGER X,Y
X = 10
Y = SQUARE(X)
WRITE(*,*) Y
END

Тогда у вас есть функция SQUARE.

FUNCTION SQUARE(N)
SQUARE = N * N
END

Они скомпилированы индивидуальноединицы.Вы можете видеть, что когда MAIN компилируется, он не ЗНАЕТ, где находится "SQUARE", по какому адресу он находится.Нужно знать, что поэтому, когда она вызывает инструкцию JUMP SUBROUTINE (JSR) для микропроцессоров, этой инструкции есть куда пойти.

.o. Файл уже содержит инструкцию JSR, но не имеет фактического значения,Это происходит позже на этапе компоновки или загрузки (в зависимости от вашего приложения).

Итак, файл MAINS .o содержит весь код для main и список ссылок, которые он хочет разрешить (особенно SQUARE).,SQUARE в основном автономен, у него нет ссылок, но в то же время у него еще нет адреса, где он находится в памяти.

Компоновщик удалит все файлы .o иобъединить их в один exe.В старые времена скомпилированный код буквально представлял собой образ памяти.Программа запускалась с какого-то адреса и просто загружалась в ОЗУ оптом, а затем выполнялась.Итак, в этом сценарии вы можете увидеть, как компоновщик берет два файла .o, объединяет их вместе (чтобы получить фактический адрес SQUARE), затем он возвращается и находит ссылку SQUARE в MAIN и заполняет адрес.

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

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

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

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

2 голосов
/ 12 марта 2011

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

The multi-part process.

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

1 голос
/ 12 марта 2011

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

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

...