Как скомпилировать несколько файлов gcc
Перейти к содержимому

Как скомпилировать несколько файлов gcc

  • автор:

Сборка программы из нескольких исходных файлов

Обычно простые программы состоят из одного исходного файла. Дело обстоит несколько сложнее, если приходиться иметь дело с крупным проектом. При работе с такой программой может возникнуть несколько достаточно серьезных проблем:

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

• Если над программой работает много человек, то практически невозможно отследить сделанные изменения.

• Процесс правки и само ориентирование при большом исходном тексте становится сложным и поиск небольшой ошибки может повлечь за собой вынужденное «изучение» кода заново.

Это далеко не все проблемы, которые могут в рассматриваемом случае. Поэтому при разработке программ рекомендуется разделять их на куски, которые

функционально ограничены и закончены.

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

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

extern int f1(); extern int f2();

printf(«f1() = %d\n»,n1); printf(«f2() = %d\n»,n2);

Теперь создаем два файла, каждый из которых будет содержать полное определение внешней функции из главной программы. Файлы назовем f1.c и f2.c :

// файл f1.c int f1() <

// файл f2.c int f2() <

Компилировать можно все файлы одновременно одной командой, перечисляя составные файлы через пробел после ключа -c :

$ gcc -c main.c f1.c f2.c

Или каждый файл в отдельности:

$ gcc -c f1.c $ gcc -c f2.c

В результате работы компилятора мы получим три отдельных объектных файла: main.o , f1.o , f2.o .

Чтобы их собрать в один файл с помощью gcc надо использовать ключ -o , при этом компоновщик соберет все файлы в один:

$ gcc main.o f1.o f2.o -o result

В результате вызова полученной программы result командой:

$ ./result f1() = 2 f2() = 10

Теперь, мы изменим какую-то из процедур, например f1() :

Компилировать заново все файлы не придется, а понадобится лишь скомпилировать измененный файл и собрать результирующий файл из частей:

$ gcc main.o f1.o f2.o -o result $ ./result

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

Оптимизация

В целях контроля времени компиляции и количества используемой памяти, а также в целях выбора разумного компромисса между скоростью выполнения и объемом конечного исполняемого файла, GCC предоставляет ряд уровней оптимизации(O1, O2, O3, O4). В дополнение к перечисленным уровням оптимизации имеются специлизированные методы оптимизации. Уровень оптимизации выбирается заданием соостветсвующего ключа -O LEVEL , где LEVEL это число от 0 до 3.

Эффекты от применения различных уровней оптимизации описаны ниже:

-O0 или отсутствие ключа опимизации (по умолчанию)

На этом уровне оптимизации GCC не производит оптимизации и компилирует исходный код наиболее простым путем. Каждая команда исходного кода преобразуется непосредственно в соответсвующую инструкцию в исполняемом файле, без каких-либо существеннх изменений. Данный режим подходит для этапа отладки программы или в случае отсутвия необходимости в оптимизации.

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

скорость выполенения программы будет выше. Более дорогостоящие виды оптимизации, такие как планирование инструкций, на данном уровне не

производятся. Компиляция с использованием ключа -O1 може занимать меньше времени, чем компиляция с клюем -O0 , в следствии уменьшения

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

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

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

Данный ключ включает более дорогостоящие методы оптимизации, такие как подстановка функции 1 , в дополнение ко всем методам применяемым на уровнях -O2 и -O1 . Уровень -O3 может увеличить скорость выполения

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

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

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

Важно помнить, что преимущества высоких уровней оптимизации могут быть невилирована их стоимостью: сложностью при отладке, высокими затратами времени и памяти при компиляции. Для большинства проектов разумно использовать оптимизацию -O0 для отладки и -O2 для сборки готового проекта.

1 Вставка тела небольших функций/методов непосредственно в место их вызова, уменьшая затраты на их вызов

Как скомпилировать несколько файлов gcc

Команда gcc предназначена для компиляции с помощью компилятора GCC кода на языке C. Данная команда похожа на команду g++, используемую для компиляции кода на языке C++.

Базовый синтаксис команды выглядит следующим образом:

$ gcc [параметры] имена файлов

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

Примеры использования

Компиляция программы из одного файла исходного кода

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

Как скомпилировать несколько файлов gcc

Это далеко не все пробемы, которые могут возникнуть при наличии программы «монстра». Поэтому при разработке программ рекомендуется их разбивать на куски, которые функционально ограничены и закончены. В этом значительно помогает сам язык C++, предоставляя свой богатый синтаксис.

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

Теперь создаем два файла, каждый из которых будет содержать полное определение внешней функции из главной программы. Файлы назовем f1.c и f2.c:

После этого процесс компиляции программы с помощью gcc будет выглядеть несколько иначе от описанного в «Шаг 1 — Компиляция программ на языке C/C++».

Компилировать можно все файлы одновременно одной командой, перечисляя составные файлы через пробел после ключа -c:

Или каждый файл в отдельности:

В результате работы компилятора мы получим три отдельных объектных файла:

Чтобы их собрать в один файл с помощью gcc надо использовать ключ -o, при этом линкер соберет все файлы в один:

В результате вызова полученной программы rezult командой:

На экране появится результат работы:

Теперь, если мы изменим какую-то из процедур, например f1():

То компилировать заново все файлы не придется, а понадобится лишь скомпилировать измененный файл и собрать результирующий файл из кусков:

Таким образом можно создавать большие проекты, которые больше не будут отнимать много времени на компиляцию и поиск ошибок. Однако помните, не стоит также черезчур разбивать программу, иначе у Вас получится несколько десятков файлов, в которых Вы сами рано или поздно запутаетесь. Найдите «золотую середину», например в отдельные файлы помещайте те функции или классы, с которыми Вам приходится больше всего работать при отладке. После того, как функция будет окончательно отлажена, ее вполне можно перенести в более крупный файл.

Русские Блоги

Начало работы с многофайловой компиляцией Linux и make-файлом

Каталог статей

1. Что такое GCC

GCC(Коллекция компиляторов GNU, Коллекция компиляторов GNU) — переводчик языков программирования, разработанный GNU. Набор компиляторов GNU включает в себя внешние интерфейсы языков C, C ++, Objective-C, Fortran, Java, Ada и Go, а также библиотеки для этих языков (например, libstdc ++, libgcj и т. Д.).
Хотя мы называем GCC компилятором языка C, процесс использования gcc для создания исполняемого файла из файла исходного кода языка C — это не просто процесс компиляции, а четыре взаимных Связанные шаги: предварительная обработка (также называемая предварительной обработкой, предварительной обработкой), компиляция, сборка и компоновка.

1. Предварительная обработка

Предварительная обработка (предварительная обработка путем чтения исходной программы на C, псевдо-инструкции (начиная с #, макрос, инструкции условной компиляции, файлы заголовков, содержащие инструкции) и специальные символы "заменяются" для создания определения макроса без Инструкции условной компиляции, выходные файлы без специальных символов.Смысл этого файла такой же, как и у исходного файла без предварительной обработки, это все еще файл C, но его содержимое отличается.
Обычно для предварительной обработки используется команда gcc -E hello.c, а параметр -E означает только предварительную обработку. Вы также можете использовать инструкцию cpp hello.c для завершения предварительной обработки, cpp — это препроцессор. Предварительно обработанный файл по-прежнему является исходным кодом на языке C, Мы можем просмотреть его код с помощью команд cat или vim. Предварительная обработка в основном включает следующие 6 аспектов:
1. Удалите все #define и разверните все определения макросов.
2. Обработайте все инструкции условной компиляции, такие как #if, #ifdef, #elif и т. д.
3. Обработайте инструкцию предварительной компиляции #include и вставьте включенный файл в позицию инструкции предварительной компиляции.
4. Удалите все комментарии «//» и «/ * * /».
5. Добавьте номера строк и идентификаторы файлов, чтобы сгенерировать номера строк для отладки и компилировать строки предупреждений об ошибках при компиляции.
6. Сохранить все#pragma директива компилятора, Потому что компилятор должен их использовать.

2. Скомпилировать

Составление (составление) — это прохождение лексического и грамматического анализа. Убедившись, что все инструкции соответствуют грамматическим правилам, Переведите его в эквивалентное промежуточное представление кода или ассемблерный код. Оптимизационная обработка — относительно сложная технология в системе компиляции. Проблемы, связанные с этим, связаны не только с самой технологией компиляции, но также имеют большое отношение к аппаратной среде машины. Часть оптимизации — это оптимизация промежуточного кода, эта оптимизация не зависит от конкретного компьютера. Другая оптимизация в основном направлена ​​на генерацию целевого кода. Для первой оптимизации основная работа заключается в удалении общих выражений, оптимизации цикла (аутсорсинг кода, ослабление силы, изменение условий управления циклом, слияние известных величин и т. Д.), Распространение копий, удаление бесполезных назначений и т. Д. Последний тип оптимизации тесно связан со структурой аппаратного обеспечения машины.Самое важное соображение состоит в том, как в полной мере использовать значения переменных, хранящихся в аппаратных регистрах машины, для уменьшения количества обращений к памяти. Кроме того, как выполнять инструкции в соответствии с характеристиками аппаратного обеспечения машины (например, конвейер
line, RISC, CISC и т. д.), а также некоторые корректировки инструкций, чтобы сделать целевой код короче и повысить эффективность выполнения, что также является важной темой исследования.
Обычно используйте команду gcc -S hello.i для компиляции и создания файлов сборки, или вы можете выполнить компиляцию с помощью указанного компилятора. Когда вы используете компилятор ПК для компиляции, он создает файлы сборки x86. Компиляция компилятора будет генерировать файлы сборки ARM. Файлы сборки каждой архитектуры могут использоваться только в этой архитектуре. Мы можем выбирать разные компиляторы в соответствии с нашей архитектурой. Этопереносимость. Процесс использования кросс-компилятора ARM на ПК для создания исполняемых программ на платформе ARM называетсяКросс-компиляция。

3. Сборка

Процесс сборки (сборки) фактически относится к Процесс перевода кода на ассемблере в целевые машинные инструкции. Для каждой исходной программы на языке C, обработанной системой перевода, соответствующий целевой файл будет окончательно обработан посредством этого процесса. В целевом файле хранится машинный код целевой программы, эквивалентной исходной программе. Целевой файл состоит из сегментов. Обычно объектный файл состоит как минимум из двух разделов:
1. Сегмент кода (текстовый сегмент): этот сегмент содержит в основном инструкции программы. Этот раздел обычно доступен для чтения и выполнения, но не для записи;
2. Сегмент данных: в основном хранит различные константы, глобальные переменные и статические данные, используемые в программе. Сегменты общих данных доступны для чтения, записи и выполнения;
Мы можем создать файлы сборки с помощью команды gcc -c hello.s.

4. Ссылка

Объектный файл, созданный ассемблером, не может быть запущен сразу, и может быть много нерешенных проблем. Например, функция в исходном файле может ссылаться на символ, определенный в другом исходном файле (например, на переменную или вызов функции и т. Д.); Функция в файле библиотеки может быть вызвана в программе и так далее. Все эти проблемы могут быть решены только обработкой программы ссылки. Основная задача компоновщика — соединить связанные объектные файлы друг с другом, то есть соединить символы, на которые есть ссылки в одном файле, с определением символа в другом файле, чтобы все эти объектные файлы стали такими, которые могут быть установлены операционной системой. Введите единое целое исполнения, то есть исполняемую программу. В соответствии с методом связывания библиотечной функции, указанным разработчиком, обработку ссылок можно разделить на два типа:
1. Статическая ссылка: все функции компилируются в программу во время компиляции.
2. Динамическая ссылка: операционная система помогает переносить динамическую библиотеку в область памяти при запуске программы.
Для вызовов функций в исполняемых файлах можно использовать динамическое связывание или статическое связывание соответственно. Использование динамического связывания может сделать конечный исполняемый файл короче и сэкономить некоторую память, когда общий объект используется несколькими процессами, потому что в памяти должна храниться только одна копия кода общего объекта. Но дело не в том, что использование динамического связывания обязательно лучше статического связывания. В некоторых случаях динамическое связывание может привести к некоторому снижению производительности.

5. Динамические и статические библиотеки

Как под Windows, так и под Linux имеется большое количество библиотек. Что такое библиотеки? По сути, библиотека — это двоичная форма исполняемого кода, который может быть загружен в память и выполнен операционной системой. Обычно мы пишем некоторые функции общего назначения в виде библиотек функций, поэтому библиотека написана другим, существующим, зрелым и повторно используемым кодом. Вы можете использовать ее, но вы должны соблюдать лицензионное соглашение. В нашем реальном процессе разработки невозможно написать каждый фрагмент кода с нуля. Когда у нас есть библиотека, мы можем напрямую связать нужные нам файлы с нашей программой. Это может сэкономить нам много времени и повысить эффективность разработки. В Linux есть два типа библиотек: статические библиотеки и динамические библиотеки. Сходство между этими двумя библиотеками заключается в том, что обе библиотеки создаются из файлов .o, Давайте обсудим их различия ниже:
1. Статическая библиотека: файл статической библиотеки назван в честь «libxxx.a», «xxx» — это имя библиотеки, а «lib» добавляется впереди, чтобы указать, что это библиотека, как в Windows, так и в Linux. Суффикс «.a» указывает на статическую библиотеку. Код статической библиотеки загружается в программу во время процесса компиляции, что приводит к большой файловой памяти после компиляции.Метод связывания статической библиотеки заключается в том, что исполняемый файл, созданный после компиляции, не нуждается в поддержке внешней библиотеки функций. Если статическая библиотека обновляется, программу необходимо повторно перевести.
2. Динамическая библиотека: имя динамической библиотеки похоже на статическую библиотеку. Оно начинается с «lib», что указывает на то, что это библиотека. Суффикс-имя в Linux — «.so (общий объект)», то есть «libXXX». .так". В Windows используется суффикс «.dll (библиотека динамической компоновки)», то есть «libXXX.dll». При компиляции динамическая библиотека не компилируется в целевой код. Вместо этого соответствующая функция в библиотеке функций вызывается, когда ваша программа выполняется для соответствующей функции. Недостатком этого является то, что функция библиотеки не интегрирована в программу. , Значит, в операционной среде программы должна быть соответствующая библиотека. Преимущество состоит в том, что изменение динамической библиотеки не влияет на вашу программу, поэтому обновление динамической библиотеки более удобно.
Между ними также есть очевидные различия: когда одна и та же программа использует статические библиотеки и динамические библиотеки для создания двух исполняемых файлов, файлы, созданные статическим связыванием Занятая память намного больше, чем файлы, созданные при динамической компоновке. Это связано с тем, что статическая компоновка компилирует все функции в программу во время компиляции, в то время как динамическая компоновка использует операционную систему, чтобы помочь перенести динамическую библиотеку в пространство памяти во время работы программы. Кроме того, если динамическая библиотека и статическая библиотека существуют одновременно, компоновщик предпочтительно использует динамическую библиотеку.

6. Общие параметры компиляции gcc

Параметры Описание
-E Только предварительная обработка, а не компиляция
-S Только компилировать, а не собирать
-c Только компилировать, собирать, а не связывать
-g Скомпилированный исполняемый файл содержит отладочную информацию gdb, которая может быть отлажена с помощью gdb.
-o Укажите имя файла скомпилированного исполняемого файла
-l Укажите каталог поиска для включаемых файлов
-L Укажите путь к библиотеке (динамической или статической), необходимой для компоновки
-ansi Стандарт ANSI
-std=c99 Стандарт C99
-Werror Не различать предупреждения и ошибки и прекращать компиляцию при обнаружении любого предупреждения
-Wall Включите большинство предупреждений
–static Статическая компиляция (по умолчанию — динамическая компиляция)
-static Статическая ссылка (по умолчанию статическая ссылка)

2. Как скомпилировать несколько файлов

1. Запись в несколько файлов

Если мы хотим скомпилировать несколько файлов, нам сначала нужно записать файл заголовка и файл .c функции-функции, а затем вызвать функцию-функцию в основной функции. Вот как это записать. Взгляните на пример.

①Функция функция .c файл

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

②Файл заголовка функции

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

③Записан основной функциональный файл

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

2. Скомпилируйте несколько файлов отдельно

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

3. Компиляция сценария оболочки

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

4. makefile

Что такое make-файл? Может быть, многие программисты Windows этого не знают, потому что эти Windows IDE делают эту работу за вас. Чтобы компилировать программное обеспечение под Unix, вы должны сами написать make-файлы, и то, можете ли вы писать make-файлы, показывает с одной стороны, есть ли у человека возможность выполнять крупномасштабные проекты. Потому что make-файл связан с правилами компиляции всего проекта. Исходные файлы в проекте не учитываются. Они размещаются в нескольких каталогах в соответствии с типом, функцией и модулем. Makefile определяет ряд правил, чтобы указать, какие файлы нужно скомпилировать в первую очередь, какие файлы нужно скомпилировать позже, а какие файлы нужно перестроить. Компилировать и даже выполнять более сложные функциональные операции, потому что make-файл похож на сценарий оболочки, в котором также могут выполняться команды операционной системы. Преимущество make-файла заключается в «автоматической компиляции». После его написания требуется только одна команда make, и весь проект полностью автоматически компилируется, что значительно повышает эффективность разработки программного обеспечения. Make — это командный инструмент, который объясняет инструкции в make-файле. Вообще говоря, в большинстве IDE есть эта команда, например make в Delphi, nmake в Visual C ++ и make в GNU под Linux. Видно, что makefile превратился в метод компиляции в инженерии.

Строки, начинающиеся с # ’#’, обозначают комментарии.
#define VAR переменной, обязательное присвоение — app VAR = test
# Добавьте значение app после значения, определенного перед VAR, затем значение переменной расширяется до testapp.
VAR+=app
# Если VAR не был определен ранее, определите и используйте testapp; в противном случае используйте предыдущее значение.
VAR?=testapp
# Первая цель — это общая цель,
#Dependency может быть файлом (каталогом) или другой целью
# Действие может быть командой Linux, и строка действия должна начинаться с клавиши TAB
target: depend1 depend2 depend3 …
[TAB] action1
[TAB ] action2
target1:
[TAB] action1
[TAB] action2

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


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

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *