Зачем сначала компилировать в объектный файл?
В прошлом году я начал программировать на Фортране, работая в исследовательском университете. Большая часть моего предыдущего опыта связана с веб-языками, такими как PHP или старый ASP, поэтому я новичок в компиляции операторов .
У меня есть два разных кода, которые я изменяю.
Один имеет явное создание оператора .o файлы из модулей (например, gfortran-c filea.f90) перед созданием исполняемого файла.
Еще являются Создание исполняемого файла непосредственно (иногда творя .файлы модов, но нет .o файлы, например gfortran-o исполняемый файл.F90 fileb.ф90 mainfile.f90).
- есть ли причина (кроме, может быть, Makefiles), что один метод предпочтительнее другого?
5 ответов:
Компиляция в объектные файлы сначала называется отдельной компиляцией. Есть много преимуществ и несколько недостатков.
Преимущества:
- легко трансформировать объектные файлы (.o) к библиотекам и ссылка на них позже
- Многие люди могут работать с разными исходными файлами одновременно
- более быстрая компиляция (вы не компилируете одни и те же файлы снова и снова, когда исходный код не изменился)
- объектные файлы могут быть созданы из различных языковых источников и связаны между собой вместе в более позднее время. Для этого объектные файлы просто должны использовать один и тот же формат и совместимые соглашения о вызовах.
- раздельная компиляция позволяет распространять системные библиотеки (библиотеки ОС, библиотеки языковых стандартов или библиотеки сторонних производителей) как статические, так и общие.
Недостатки:
- есть некоторые оптимизации (например, оптимизация функций), которые компилятор не может выполнить, и компоновщик не заботится о них; однако многие из них компиляторы теперь включают опцию для выполнения "оптимизации времени соединения", которая в значительной степени устраняет этот недостаток. Но это все еще проблема для системных библиотек и сторонних библиотек, особенно для общих библиотек (невозможно оптимизировать удаленные части компонента, которые могут изменяться при каждом запуске, однако другие методы, такие как JIT-компиляция, могут смягчить это).
- в некоторых языках программист должен предоставить какой-то заголовок для использования других, которые будут связаны с этим объектом. Для пример в C вы должны предоставить
.h
файлы, чтобы идти с вашими объектными файлами. Но это все равно хорошая практика.- в языках с текстовой основой, таких как C или C++, если вы изменяете прототип функции, вы должны изменить его в двух местах. Один раз в заголовочном файле, один раз в файле реализации.
Когда у вас есть проект с несколькими 100 исходными файлами, вы не хотите перекомпилировать все из них при каждом изменении. Компилируя каждый исходный файл в отдельный объектный файл и перекомпилируя только те исходные файлы, на которые повлияло изменение, вы тратите минимальное количество времени от изменения исходного кода до Нового исполняемого файла.
make
является распространенным инструментом, используемым для отслеживания таких зависимостей и воссоздания вашего двоичного файла, когда что-то меняется. Как правило, вы устанавливаете, что каждый источник файл зависит от (эти зависимости обычно могут быть сгенерированы вашим компилятором-в формате, подходящем дляmake
), и пусть make обрабатывает детали создания обновленного двоичного файла.
The .o file-это объектный файл. Это промежуточное представление конечной программы.
Конкретно, как правило, то .o файл имеет скомпилированный код, но он не имеет окончательных адресов для всех различных подпрограмм или данных.
Одна из вещей, которая необходима программе перед запуском, - это нечто похожее на образ памяти.
Например.
Если у вас есть основная программа и она вызывает подпрограмму A. (Это искусственный Фортран, я его не трогал десятилетия, так что работай со мной здесь.)
PROGRAM MAIN INTEGER X,Y X = 10 Y = SQUARE(X) WRITE(*,*) Y END
Тогда у вас есть квадратная функция.
FUNCTION SQUARE(N) SQUARE = N * N END
Это индивидуально скомпилированные единицы измерения. Вы можете видеть, что при компиляции MAIN он не знает, где находится "квадрат", по какому адресу он находится. Он должен знать, что поэтому, когда он вызывает инструкцию микропроцессорной подпрограммы перехода (JSR), инструкции есть куда идти.
The .o файл уже содержит инструкцию JSR, но у него нет фактического значения. Это приходит позже в соединении или фаза загрузки (в зависимости от вашего приложения).
Итак, сеть .o файл содержит весь код main и список ссылок, которые он хочет разрешить (в частности, квадрат). Квадрат в основном стоит отдельно, у него нет никаких ссылок, но в то же время у него еще не было адреса, где он существует в памяти.
Компоновщик снимет все с себя .o файлы и объединить их в один исполняемый файл. В старые времена скомпилированный код был бы буквально образом памяти. Программа должна была начаться по какому-то адресу и просто загрузили в оперативку оптом,а потом выполнили. Итак, в сценарии вы можете увидеть, как компоновщик берет два .o файлы, объединяя их вместе (чтобы получить фактический адрес квадрата), затем он вернется и найдет квадратную ссылку в MAIN, и заполнит адрес.
Современные компоновщики не заходят так далеко и откладывают большую часть окончательной обработки до момента фактической загрузки программы. Но концепция схожа.Путем компиляции к.o файлы, вы в конечном итоге с многократно используемыми логическими блоками, которые затем объединяются процессами связывания и загрузки перед выполнением.
Другой приятный аспект заключается в том, что.o файлы могут поступать с разных языков. До тех пор, пока вызывающие механизмы совместимы (то есть как аргументы передаются в функции и процедуры и из них), то после компиляции в a .о, язык становится менее актуальной. Вы можете связать, объединить код C с кодом FORTRAN, скажем.
В PHP и все, процесс отличается тем, что весь код загружается в один образ во время выполнения. Вы можете рассмотреть Фортранс .o файлы, подобные тому, как вы используете PHPs include механизм для объединения файлов в большое, связное целое.
Еще одна причина, помимо времени компиляции, заключается в том, что процесс компиляции являетсямногоступенчатым процессом .
Объектные файлы являются лишь одним промежуточным результатом этого процесса. В конечном счете они будут использованы компоновщиком для создания исполняемого файла.
Мы компилируем в объектные файлы, чтобы иметь возможность связать их вместе, чтобы сформировать большие исполняемые файлы. Это не единственный способ сделать это.
Существуют также компиляторы, которые не делают этого таким образом, а вместо этого компилируют в память и немедленно выполняют результат. Раньше, когда студенты должны были использовать мэйнфреймы, это было стандартным. Turbo Pascal также сделал это таким образом.