Вы, кажется, есть небольшое неправильное представление о том, как компиляции и компоновки работы.
Предположим, что файлы выглядеть следующим образом:
//a.h
// nothing in particular
//a.c
#include "b.h"
int main()
{
bravo();
return 0;
}
//b.h
void bravo();
//b.c
#include "c.h"
void bravo()
{
charlie();
}
//c.h
void charlie();
//c.c
#include "d.h"
void charlie()
{
delta();
}
и так далее.
Как вы можете видеть, код в a.c
относится к коду объявлен в b.h
и определенной в b.c
. Это не относится ни к чему в c
или d
.
Вы можете построить a.o
по составлению [1]:
gcc -c a.c
«-c» означает «создать файл двоичный объект и остановить, не пытайтесь построить исполняемый». Компилятор замечает строку
#include "b.h"
и тянет «b.h» в код. Он не смотрит на b.c
, потому что никто не сказал об этом. Он видит, что этот код использует bravo()
, который не был определен, и он надеется, что кто-то предоставит эту функцию, когда придет время для создания исполняемого файла. Компилятор не знает и не заботится о b.c
, charlie()
, delta()
и ни в одном из c
или d
файлах.
Когда приходит время для создания исполняемого файла, то линкер вступает в игру [2]:
gcc a.o b.o c.o d.o
Компоновщик может построить исполняемый файл из этих файлов, так как все функции, которые вызываются, определяются, поэтому он может связывать все свободные концы.Это:
gcc a.o b.o
не будет работать. Компилятор говорит: «Держись, я не могу построить полную программу из этого, вы вызываете charlie()
, но я не вижу определения charlie()
!»
[1] Есть некоторые разногласия относительно того, следует ли это называть «компиляция a.c
» или «компиляция a.o», но это не важно здесь.
[2] Обратите внимание, что gcc содержит как компилятор, так и компоновщик, а также другие вещи. Это как швейцарский армейский нож.
Объясните, что вы подразумеваете под «где использует b, b использует c, c использует d.», У вас есть три программы или библиотеки? Если вам нужно связать подмножество объектных файлов, тогда просто скажите так: someapp: a.o b.o'. – user657267