我有两个目录sorting
和searching
(同一目录的子目录),有.c
个源文件和.h
个头文件:
mbp:c $ ls sorting
array_tools.c bubble_sort.c insertion_sort.c main selection_sort.c
array_tools.h bubble_sort.h insertion_sort.h main.c selection_sort.h
mbp:c $ ls searching
array_tools.c array_tools.h binary_search.c binary_search.h linear_search.c linear_search.h main main.c
在searching
内,我正在构建一个需要使用insertion_sort
函数的可执行文件,该函数在insertion_sort.h
中声明并在insertion_sort.c
sorting
内定义。以下编译成功生成可执行文件:
mbp:searching $ clang -Wall -pedantic -g -iquote"../sorting" -o main main.c array_tools.c binary_search.c linear_search.c ../sorting/insertion_sort.c
但是,我希望能够通过使用#include
包含标头然后为编译器提供搜索路径来包含来自任意目录的函数。我是否需要预先将.c
文件预编译为.o
个文件? clang的man
页面列出以下选项:
-I<directory>
Add the specified directory to the search path for include files.
但是下面的编译失败了:
mbp:searching $ clang -Wall -pedantic -g -I../sorting -o main main.c array_tools.c binary_search.c linear_search.c
Undefined symbols for architecture x86_64:
"_insertion_sort", referenced from:
_main in main-1a1af0.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
main.c
包含以下include
s:
#include <stdio.h>
#include <stdlib.h>
#include "linear_search.h"
#include "binary_search.h"
#include "array_tools.h"
#include "insertion_sort.h"
我不理解头文件,源文件和目标文件之间的链接。要包含在.c
文件中定义的函数,只要.c
文件与标题位于同一目录中,是否包含同名头文件就足够了?我已经在SO上阅读了多个答案,clang的man
页面和一些教程,但无法找到明确,明确的答案。
回应@ spectras :
逐个给编译器一个源文件。例如:
cc -Wall -Ipath/to/some/headers foo.c -o foo.o
正在运行
mbp:sorting $ clang -Wall insertion_sort.c -o insertion_sort.o
产生以下错误:
Undefined symbols for architecture x86_64:
"_main", referenced from:
implicit entry/start for main executable
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
答案 0 :(得分:3)
好吧,它有点混淆了。让我们看看如何编写一个简单的多文件项目。
逐个给编译器一个源文件。例如:
cc -c -Wall -Ipath/to/some/headers foo.c -o foo.o
-c
标志告诉编译器你想要一个目标文件,所以它不应该运行链接器。
编译器在源文件上运行预处理器。除此之外,每次看到#include
指令时,它都会搜索指定文件的包含路径,并基本上复制粘贴它,将#include
替换为内容。这是递归完成的。
这是将您包含的所有.h
合并到源文件中的步骤。我们把整个事情称为翻译单位。
您可以使用-E
标志查看此步骤的结果并检查结果,例如:
cc -Wall -Ipath/to/some/headers foo.c -E -o foo.test
让我们简短一点,因为其他步骤与您的问题无关。然后,编译器根据生成的源代码创建一个目标文件。目标文件包含翻译单元中所有代码和数据的二进制版本,以及用于将所有内容放在一起的元数据以及其他一些内容(如调试信息)。
您可以使用objdump -xd foo.o
检查目标文件的内容。
请注意,由于这是针对每个源文件执行的,这意味着会一次又一次地反复解析和编译标头。这就是他们应该只声明东西并且不包含实际代码的原因:你最终会在每个目标文件中使用该代码。
完成后,将所有目标文件链接到可执行文件中,例如:
cc foo.o bar.o baz.o -o myprogram
此步骤将收集所有内容,解决依赖关系并将所有内容写入可执行二进制文件。您也可以使用-l
提取外部对象文件,例如-lrt
或-lm
。
例如:
void do_bar(int);
do_bar
do_bar
。do_bar
,我明白了。”do_bar
的实际调用替换占位符。最后,当您将多个.c
文件传递给编译时,就像您在问题中所做的那样,编译器基本上做同样的事情,只是它不会生成中间对象文件。总体过程表现相同。
那么,你的错误呢?
Undefined symbols for architecture x86_64:
"_insertion_sort", referenced from:
_main in main-1a1af0.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
请参阅?它说连接步骤失败了。这意味着前一步进展顺利。 #include
有效。它只是在链接步骤中,它正在寻找一个名为_insertion_sort
的符号(数据或代码),但却找不到它。那是因为该符号在某处被声明(否则使用它的源将不会编译),但它的定义不可用。没有源文件实现它,或者包含它的目标文件没有提供给链接器。
=&GT;您需要提供_insertion_sort
的定义。通过将../sorting/insertion_sort.c
添加到您传递的源列表中,或者将其编译到目标文件中并传递它。或者将它构建到一个库中,这样它就可以被你的两个二进制文件共享(否则它们每个都会嵌入一个副本)。
当你到达那里时,通常开始使用诸如CMake
之类的构建工具套件是一个好主意。它将为您处理所有细节。