Как компилятор C / C ++ находит определения прототипов в заголовочных файлах? - PullRequest
21 голосов
/ 30 июля 2010

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

Ответы [ 3 ]

24 голосов
/ 30 июля 2010

Компилятор этого не делает, компоновщик делает.

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

Например, скажем, у вас есть foo.h и foo.c, определяющие и реализующие функцию foo(), и bar.h и bar.c, определяющие и реализующие bar(). Скажем, bar звонит foo, чтобы bar.c включал foo.h. Этот сборник состоит из трех шагов:

gcc -c foo.c
gcc -c bar.c
gcc foo.o bar.o -o program

Первая строка компилирует foo.c, производя foo.o. Второй компилирует bar.c, производя bar.o. На данный момент в объектном файле bar.o foo является внешним символом. Третья строка вызывает компоновщик, который связывает вместе foo.o и bar.o в исполняемый файл с именем «program». Когда компоновщик обрабатывает bar.o, он видит неразрешенный внешний символ foo и, таким образом, он просматривает таблицу символов всех других связанных объектных файлов (в данном случае просто foo.o) и находит foo в foo .o, и завершает ссылку.

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

14 голосов
/ 30 июля 2010

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

Компоновщик берет все файлы .obj, которые были выведены компилятором, а затем (как следует из названия) связывает все вместе. Таким образом, для каждого модуля он просматривает список символов, помеченных как «определенные извне», и просматривает все другие модули, которые он был задан для этих символов.

Таким образом, он только «ищет» модули, в которых вы сказали, что он ищет.

Если он не может найти символ ни в одном из других модулей, тогда вы получите ошибку «неопределенная ссылка».

0 голосов
/ 30 июля 2010

Предположим, у вас есть foo.cpp с #include foo.h и, возможно, другими включениями. Заголовки, конечно, могут иметь свои собственные # include-s.

Препроцессор запустится с foo.cpp, проанализирует #includes и прочитает содержимое заголовка. Результатом будет текст из заголовочных файлов и foo.cpp "сплющенный". Затем компилятор отработает этот текст. Если переменная / функция / etc должна иметь было объявлено где-то в заголовке не найдено, компилятор сообщит об ошибке.

Суть в том, что компилятор должен видеть все свои объявления в результате .cpp и заголовков.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...