От исходного кода к исполняемому файлу обычно является двухэтапным процессом для C и связанных языков, хотя в среде IDE это, вероятно, представляется как один процесс.
1 / Вы кодируете свой исходный код и запускаете его через компилятор. На этом этапе компилятору нужны ваш исходный код и файлы заголовков других материалов, с которыми вы собираетесь связать (см. Ниже).
Компиляция состоит из превращения ваших исходных файлов в объектные файлы. В объектных файлах есть ваш скомпилированный код и достаточно информации, чтобы знать, что им еще нужно, но , а не , где можно найти другие вещи (например, библиотеки LUA).
2 / На следующем этапе компоновка объединяет все ваши объектные файлы с библиотеками для создания исполняемого файла. Я не буду описывать динамические ссылки здесь, поскольку это усложнит объяснение с небольшой выгодой.
Вам нужно не только указать каталоги, в которых компоновщик может найти другой код, вы также должны указать фактическую библиотеку, содержащую этот код. Тот факт, что вы получаете неразрешенные внешние признаки, указывает на то, что вы этого не сделали.
В качестве примера рассмотрим следующий упрощенный код C (xx.c
) и команду.
#include <bob.h>
int x = bob_fn(7);
cc -c -o xx.obj xx.c
Компилирует файл xx.c
в xx.obj
. bob.h
содержит прототип для bob_fn()
, поэтому компиляция будет успешной. -c
указывает компилятору генерировать объектный файл, а не исполняемый файл, а -o xx.obj
устанавливает имя выходного файла.
Но фактический код для bob_fn()
находится не в заголовочном файле, а в /bob/libs/libbob.so
, поэтому для связи вам нужно что-то вроде:
cc -o xx.exe xx.obj -L/bob/libs;/usr/lib -lbob
Создает xx.exe
из xx.obj
, используя библиотеки (ищутся по заданным путям) вида libbob.so
(lib и .so обычно добавляются компоновщиком). В этом примере -L
устанавливает путь поиска для библиотек. -l
указывает библиотеку, которую необходимо найти для включения в исполняемый файл, если это необходимо. Компоновщик обычно берет «bob» и находит первый соответствующий файл библиотеки в пути поиска, указанном -L
.
Файл библиотеки на самом деле представляет собой набор объектных файлов (что-то вроде того, как zip-файл содержит несколько других файлов, но не обязательно сжатых) - когда обнаруживается первое соответствующее вхождение неопределенного внешнего объекта, объектный файл копируется из библиотека и добавлен в исполняемый файл, как ваш xx.obj
файл. Обычно это продолжается до тех пор, пока не останется нерешенных внешних факторов. «Соответствующая» библиотека - это модификация текста «bob», она может выглядеть как libbob.a
, libbob.dll
, libbob.so
, bob.a
, bob.dll
, bob.so
и так далее. Актуальность определяется самим компоновщиком и должна быть задокументирована.
Как это работает, зависит от компоновщика, но это в основном все.
1 / Все ваши объектные файлы содержат список неразрешенных внешних объектов, которые им необходимо разрешить. Компоновщик объединяет все эти объекты и исправляет связи между ними (разрешает как можно больше внешних объектов).
2 / Затем для каждого внешнего , все еще неразрешенного, компоновщик объединяет библиотечные файлы в поисках объектного файла, который может удовлетворить ссылку. Если он находит его, он втягивает его - это может привести к дальнейшим неразрешенным внешним воздействиям, поскольку объект, к которому он подключен, может иметь собственный список внешних элементов, которые должны быть удовлетворены.
3 / Повторяйте шаг 2 до тех пор, пока не останется неразрешенных внешних элементов или не будет возможности разрешить их из списка библиотек (именно в этом ваша разработка, поскольку вы не включили файл библиотеки LUA).
Сложность, о которой я упоминал ранее, это динамическое связывание. Здесь вы связываете заглушку подпрограммы (своего рода маркер), а не фактическую подпрограмму, которая позже разрешается во время загрузки (когда вы запускаете исполняемый файл). Такие вещи, как общие элементы управления Windows, находятся в этих библиотеках DLL, так что они могут меняться без необходимости связывать объекты в новый исполняемый файл.