Как происходит процесс компиляции файлов cpp в двоичный файл

Обновлено: 21.11.2024

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

2.2.1 Синтаксис команды

  • Все параметры компоновщика и параметры -I , -L , -pti и -R накапливаются, но не переопределяют.
  • Все опции -U обрабатываются после всех опций -D.

2.2.2 Соглашения об именах файлов

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

ТАБЛИЦА 2-1 Суффиксы имен файлов, распознаваемые компилятором C++

2.2.3 Использование нескольких исходных файлов

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

2.2.4 Компиляция с использованием разных версий компилятора

Начиная с компилятора Sun WorkShop 6 C++, компилятор помечает каталог кэша шаблонов строкой, которая определяет версию кэша шаблонов.

Этот компилятор проверяет версию каталога кэша и выдает сообщения об ошибках при каждом обнаружении проблемы с версией кеша. Будущие компиляторы Sun WorkShop C++ также будут проверять версии кэша. Например, будущий компилятор, который имеет другую идентификацию версии кэша шаблона и обрабатывает каталог кэша, созданный этой версией компилятора, может выдать следующую ошибку:

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

Хотя каталоги кэша шаблонов, созданные компилятором Sun WorkShop C++ 5.0, не помечены идентификаторами версии, Sun WorkShop 6 C++ компилятор обрабатывает каталоги кеша 5.0 без ошибок или предупреждений. Компилятор Sun WorkShop 6 C++ преобразует каталоги кэша 5.0 в формат каталога, используемый компилятором Sun WorkShop 6 C++.

Компилятор Sun WorkShop C++ 5.0 не может использовать каталог кэша, созданный Sun WorkShop 6. Компилятор C++ или более поздняя версия. Компилятор Sun WorkShop C++ версии 5.0 не способен распознавать различия в форматах и ​​выдает подтверждение при обнаружении каталога кэша, созданного компилятором Sun WorkShop 6 C++ или более поздней версией.

При обновлении компиляторов всегда рекомендуется запускать CCadmin -clean для каждого каталога, содержащего каталог кэша шаблонов (в большинстве случаев каталог кэша шаблонов называется SunWS_cache). В качестве альтернативы вы можете использовать rm -rf SunWS_cache .

2.3 Компиляция и компоновка

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

2.3.1 Последовательность компиляции

В предыдущем примере компилятор автоматически генерирует объектные файлы загрузчика ( file1.o , file2.o и file3.o ), а затем вызывает системный компоновщик для создания исполняемой программы для файла prgrm.

< p>После компиляции объектные файлы (file1.o, file2.o и file3.o) остаются. Это соглашение позволяет легко повторно связывать и перекомпилировать ваши файлы.

Примечание. Если компилируется только один исходный файл и программа компонуется в одной и той же операции, соответствующий файл .o удаляется автоматически. Чтобы сохранить все файлы .o, не выполняйте компиляцию и компоновку в одной и той же операции, пока не будет скомпилировано более одного исходного файла.

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

2.3.2 Раздельная компиляция и компоновка

Компиляцию и компоновку можно выполнять отдельными шагами. Параметр -c компилирует исходные файлы и создает объектные файлы .o, но не создает исполняемый файл. Без параметра -c компилятор вызывает компоновщик. Благодаря разделению этапов компиляции и компоновки полная перекомпиляция не требуется только для исправления одного файла. В следующем примере показано, как скомпилировать один файл и связать с другими в отдельные шаги:

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

2.3.3 Согласованная компиляция и компоновка

  • -быстро
  • -g0
  • -библиотека
  • -неверное выравнивание
  • -мт
  • -xa
  • -xarch =isa
  • -xcg92 и -xcg89
  • -xpg
  • -xprofile
  • -xtarget= t
  • -xvector или -xvector=yes
  • В случае с параметрами -library , -fast и -xarch вы должны обязательно включить параметры компоновщика, которые были бы переданы, если бы вы скомпилировали и скомпоновали вместе.
  • С параметрами -p , -xpg и -xprofile включение параметра на одном этапе и его исключение на другом этапе не повлияет на правильность программы, но вы не сможете выполнять профилирование.
  • С параметрами -g и -g0 включение параметра на одном этапе и его исключение на другом этапе не повлияет на правильность программы, но программа не будет должным образом подготовлена ​​для отладки.

2.3.4 Компиляция для SPARC V9

Компиляция, компоновка и выполнение 64-разрядных объектов поддерживаются только в среде V9 SPARC, Solaris 7 или Solaris 8 с работающим 64-разрядным ядром. Компиляция для 64-разрядной версии указывается параметрами -xarch=v9 , -xarch=v9a и -xarch=v9b.

2.3.5 Диагностика компилятора

  • Нераспознанные параметры, которым предшествует тире (-) или знак плюса (+), вызывают предупреждения.
  • Нераспознанные непараметры, которым не предшествует тире или знак плюса, не вызывают предупреждений. (Однако они передаются компоновщику. Если компоновщик их не распознает, они генерируют сообщения об ошибках компоновщика.)

2.3.6 Понимание организации компилятора

Пакет компилятора C++ состоит из внешнего интерфейса, оптимизатора, генератора кода, ассемблера, модуля предварительной компоновки шаблонов и редактора ссылок. Команда CC вызывает каждый из этих компонентов автоматически, если вы не используете параметры командной строки, чтобы указать иное. РИСУНОК 2-1 показывает порядок, в котором компоненты вызываются компилятором.

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


РИСУНОК 2-1 Процесс компиляции

Как показано в следующей таблице, входные файлы для различных компонентов компилятора имеют разные суффиксы имен файлов. Суффикс определяет тип выполняемой компиляции. Обратитесь к ТАБЛИЦЕ 2-1, чтобы узнать значения файловых суффиксов.

ТАБЛИЦА 2-2 Компоненты системы компиляции C++

2.4 Требования к памяти

  • Размер каждой процедуры
  • Уровень оптимизации
  • Ограничения, установленные для виртуальной памяти
  • Размер файла подкачки диска

2.4.1 Размер пространства подкачки

Команда swap -s отображает доступное пространство подкачки. Для получения дополнительной информации см. справочную страницу swap (1M).

Следующий пример демонстрирует использование команды swap:

2.4.2 Увеличение пространства подкачки

Используйте mkfile (1M) и swap (1M), чтобы увеличить размер области подкачки на рабочей станции. (Для этого вы должны стать суперпользователем.) Команда mkfile создает файл определенного размера, а swap -a добавляет файл в пространство подкачки системы:

2.4.3 Управление виртуальной памятью

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

Чтобы ограничить виртуальную память в оболочке sh, используйте команду ulimit. См. справочную страницу sh (1) для получения дополнительной информации.

В следующем примере показано, как ограничить виртуальную память до 16 Мбайт:

В оболочке csh используйте команду limit для ограничения виртуальная память. См. справочную страницу csh (1) для получения дополнительной информации.

Следующий пример также показывает, как ограничить виртуальную память до 16 Мбайт:

Каждый из этих примеров заставляет оптимизатор пытаться восстанавливаться при 16 Мбайт пространства данных.

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

Убедитесь, что ни одна компиляция не занимает более половины пространства подкачки.

При наличии 32 Мбайт пространства подкачки используйте следующие команды:

В оболочке sh:

В оболочке csh:

Наилучшая настройка зависит от требуемой степени оптимизации и объема доступной реальной и виртуальной памяти.

2.4.4 Требования к памяти

Рабочая станция должна иметь не менее 24 мегабайт памяти; Рекомендуется 32 МБ.

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

2.5 упрощенных команд

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

2.5.1 Использование псевдонимов в оболочке C

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

В следующем примере используется псевдоним CCfx .

Команда CCfx теперь такая же, как:

2.5.2 Использование CCFLAGS для указания параметров компиляции

Вы можете указать параметры, задав переменную CCFLAGS.

Переменную CCFLAGS можно явно использовать в командной строке. В следующем примере показано, как установить CCFLAGS (C Shell):

В следующем примере CCFLAGS используется явно.

При использовании make , если переменная CCFLAGS установлена, как в предыдущем примере и правила компиляции make-файла являются неявными, то вызов make приведет к компиляции, эквивалентной:

CC -xO2 -xsb files.

2.5.3 Использование make

Утилита make — очень мощное средство разработки программ, которое можно легко использовать со всеми компиляторами Sun. Дополнительные сведения см. на справочной странице make (1S).

2.5.3.1 Использование CCFLAGS внутри make

Если вы используете неявные правила компиляции файла makefile (то есть нет строки компиляции C++), программа make автоматически использует CCFLAGS.

2.5.3.2 Добавление суффикса в ваш Makefile

Вы можете включать различные файловые суффиксы в C++, добавляя их в make-файл. В следующем примере в качестве допустимого суффикса для файлов C++ добавляется .cpp. Добавьте макрос SUFFIXES в ваш make-файл:

(Эта строка может располагаться в любом месте make-файла.)

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

Важно понимать, что, хотя некоторые компьютерные языки (например, схема или базовый) обычно используются с интерактивным интерпретатором (где вы вводите команды, которые немедленно выполняются), C++ не работает. сюда. Файлы исходного кода C++ всегда компилируются в двоичный код программой, называемой «компилятором», а затем выполняются. На самом деле это многоэтапный процесс, который мы подробно опишем здесь.

Различные типы файлов

  1. Обычные файлы исходного кода. Эти файлы содержат определения функций и имеют имена, оканчивающиеся на ".cc" по соглашению (хотя иногда вы будете видеть имена файлов исходного кода, которые заканчиваются на ".cpp" или ".C").

Препроцессор

Создание объектного файла: компилятор

В общем случае компилятор вызывается следующим образом: где "%" — это приглашение unix. Это говорит компилятору запустить препроцессор в файле foo.cc, а затем скомпилировать его в файл объектного кода foo.o. Параметр -c означает компиляцию файла исходного кода в объектный файл, но не вызов компоновщика. Если вся ваша программа находится в одном файле с исходным кодом, вместо этого вы можете сделать следующее: Это говорит компилятору запустить препроцессор на foo.cc, скомпилировать его, а затем скомпоновать его для создания исполняемого файла с именем foo. Опция -o указывает, что следующее слово в строке — это имя бинарного исполняемого файла (программы). Если вы не укажете -o, то есть если вы просто наберете g++ foo.cc, исполняемый файл будет называться a.out по глупым историческим причинам.

Обратите также внимание, что имя используемого нами компилятора — g++, что связано с компилятором GNU C gcc (у ​​него большая часть внутренних компонентов совпадает с gcc).

Все вместе: компоновщик

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

Как и препроцессор, компоновщик представляет собой отдельную программу под названием ld. Как и препроцессор, компоновщик вызывается автоматически при использовании компилятора. Обычный способ использования компоновщика следующий: Эта строка указывает компилятору скомпоновать вместе три объектных файла (foo.o, bar.o и baz.o) в двоичный исполняемый файл с именем myprog. Теперь у вас есть файл с именем myprog, который вы можете запустить и который, надеюсь, сделает что-то классное и/или полезное.

Это все, что вам нужно знать, чтобы начать компилировать свои собственные программы на C++. Как правило, мы также рекомендуем вам использовать параметр командной строки -Wall: параметр -Wall заставляет компилятор предупреждать вас о допустимых, но сомнительных конструкциях кода и поможет вам выявить множество ошибок на самых ранних этапах.

При разработке программы на C++ следующим шагом является компиляция программы перед ее запуском.Компиляция — это процесс, который преобразует программу, написанную на удобочитаемом языке, таком как C, C++ и т. д., в машинный код, непосредственно понятный центральному процессору. Например, если у вас есть файл исходного кода C++ с именем prog.cpp и вы выполняете команду компиляции,

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

Расширенный файл исходного кода C++, созданный препроцессором C++, компилируется в язык ассемблера для платформы.

Код ассемблера, сгенерированный компилятором, собирается в объектный код для платформы.

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

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

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

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

Подборка

Этап компиляции выполняется для каждого выхода препроцессора. Компилятор анализирует исходный код на чистом C++ (теперь без каких-либо директив препроцессора) и преобразует его в ассемблерный код. Затем вызывает базовый сервер (ассемблер в наборе инструментов), который собирает этот код в машинный код, создавая фактический двоичный файл в некотором формате (ELF, COFF, a.out, . ). Этот объектный файл содержит скомпилированный код (в двоичной форме) символов, определенных во входных данных. Символы в объектных файлах упоминаются по имени.

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

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

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

Именно на этом этапе сообщается о "обычных" ошибках компилятора, таких как синтаксические ошибки или ошибки разрешения перегрузки.

Чтобы остановить процесс после этапа компиляции, мы можем использовать параметр -S:

Сборка

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

Чтобы остановить процесс после этапа сборки, вы можете использовать параметр -c:

Связывание

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

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

(Примечание. Это запись в разделе часто задаваемых вопросов по C++ Stack Overflow. Если вы хотите покритиковать идею предоставления FAQ в такой форме, то публикация в мета, с которой все это началось, быть местом, чтобы сделать это Ответы на этот вопрос отслеживаются в чате C++, где идея часто задаваемых вопросов зародилась в первую очередь, поэтому ваш ответ, скорее всего, будет прочитан теми, кто придумал эту идею.)

5 ответов 5

Компиляция программы C++ включает три этапа:

Компиляция: компилятор берет выходные данные препроцессора и создает из них объектный файл.

Связывание: компоновщик берет объектные файлы, созданные компилятором, и создает либо библиотеку, либо исполняемый файл.

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

Этап компиляции выполняется для каждого выхода препроцессора. Компилятор анализирует исходный код на чистом C++ (теперь без каких-либо директив препроцессора) и преобразует его в ассемблерный код.Затем вызывает базовый сервер (ассемблер в наборе инструментов), который собирает этот код в машинный код, создавая фактический двоичный файл в некотором формате (ELF, COFF, a.out, . ). Этот объектный файл содержит скомпилированный код (в двоичной форме) символов, определенных во входных данных. Символы в объектных файлах упоминаются по имени.

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

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

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

Именно на этом этапе сообщается о "обычных" ошибках компилятора, таких как синтаксические ошибки или ошибки разрешения перегрузки.

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

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

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

Где применяется оптимизация? На первый взгляд кажется, что это делается на этапе компиляции, но, с другой стороны, я могу себе представить, что правильная оптимизация может быть выполнена только после компоновки.

@BartvanHeukelom традиционно это делалось во время компиляции, но современные компиляторы поддерживают так называемую «оптимизацию времени компоновки», преимущество которой заключается в возможности оптимизации между единицами перевода.

Если компоновщик преобразует символы, относящиеся к классам/методам в библиотеках, в адреса, означает ли это, что двоичные файлы библиотек хранятся в адресах памяти, которые ОС поддерживает постоянными? Я просто не понимаю, как компоновщик узнает точный адрес, скажем, двоичного файла stdio для всех целевых систем. Путь к файлу всегда будет одним и тем же, но точный адрес может измениться, верно?

Вот что написал автор:

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

Под компиляцией понимается обработка файлов исходного кода (.c, .cc или .cpp) и создание «объектного» файла. Этот шаг не создает ничего, что может запустить пользователь. Вместо этого компилятор просто создает инструкции машинного языка, соответствующие скомпилированному файлу исходного кода. Например, если вы скомпилируете (но не свяжете) три отдельных файла, в качестве вывода будут созданы три объектных файла, каждый с именем .o или .obj (расширение зависит от вашего компилятора). Каждый из этих файлов содержит перевод файла вашего исходного кода в файл машинного языка, но вы пока не можете их запустить! Вам нужно превратить их в исполняемые файлы, которые может использовать ваша операционная система. Вот где в дело вступает компоновщик.

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

Вы можете спросить, почему существуют отдельные этапы компиляции и компоновки. Во-первых, вероятно, так проще реализовать вещи. Компилятор делает свое дело, а компоновщик делает свое дело — разделение функций снижает сложность программы.Еще одно (более очевидное) преимущество заключается в том, что это позволяет создавать большие программы без повторного выполнения шага компиляции каждый раз при изменении файла. Вместо этого, используя так называемую «условную компиляцию», необходимо скомпилировать только те исходные файлы, которые изменились; в остальном объектные файлы являются достаточным входом для компоновщика. Наконец, это упрощает реализацию библиотек предварительно скомпилированного кода: просто создайте объектные файлы и свяжите их так же, как любой другой объектный файл. (Кстати, тот факт, что каждый файл компилируется отдельно от информации, содержащейся в других файлах, называется "моделью раздельной компиляции".)

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

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

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