使用头文件时出现未定义符号错误

8
我遇到了以下错误,但是我却无法想出自己做错了什么。
$ gcc main.c -o main

Undefined symbols:
  "_wtf", referenced from:
      _main in ccu2Qr2V.o
ld: symbol(s) not found
collect2: ld returned 1 exit status

main.c:

#include <stdio.h>
#include "wtf.h"

main(){
    wtf();
}

wtf.h:

void wtf();

wtf.c:

void wtf(){
    printf("I never see the light of day.");
}

现在,如果我将整个函数包含在头文件中而不仅仅是签名,它可以编译通过,所以我知道wtf.h已经被包含了。为什么编译器看不到wtf.c呢?还是我漏掉了什么?
问候。

2
你从未告诉编译器编译wtf.c文件,这就是问题所在。 - Michael Foukarakis
1
是的,我来自魔法和独角兽之地,所以我只是假设将实现命名为与头文件相同,可以让编译器找到两者。 - Chris Cummings
2个回答

14
你需要将 wtf与你的 main 进行链接。最简单的方式是一起编译 - gcc会帮助你进行链接,如下所示:
gcc main.c wtf.c -o main

更长的方式(将wtf分开编译):

gcc -c wtf.c
gcc main.c wtf.o -o main

更长的代码(分离编译和链接)

gcc -c wtf.c
gcc -c main.c
gcc main.o wtf.o -o main

你可以直接运行ld而不是最后一次的gcc调用,效果相同。


那么,有什么阻止我做以下操作的吗?#include <stdio.h> #include "wtf.h" #include "wtf.c"这感觉不太好,但当我运行gcc时,为每个实现都包含一个参数的想法也是如此。 - Chris Cummings
2
@j33r:假设没有任何事情阻止_你_。 然而,我们需要靠编写C++代码赚钱谋生的人们却有我们的同事阻止我们这样做,以保护他们的理智。 (如果您真的不知道:C的一个关键特性是_分离编译_。 其中之一是它允许我们处理预处理器文件包含所带来的疯狂编译时间。 还有信息隐藏、封装和其他好的 - 不幸的是,经常被低估的 - 原则。) - sbi
1
@j33r:当你的程序有多个.c文件时,就该写一个简单的Makefile来构建它。 - caf

4
你忽略了一个事实,仅仅包含头文件并不能告诉编译器有关在头文件中声明的实际实现(定义)在哪里的任何信息。
它们可能在与执行包含操作的C文件相邻的文件中,它们可能来自预编译的静态链接库,或者是由系统链接器在读取可执行文件时加载的动态库,或者它们可能在运行时由用户程序员控制的显式动态加载(例如Linux中的dlopen()函数系列)。
C不像Java,没有隐含的规则认为只因为一个C文件包含了某个头文件,编译器也应该做一些“神奇”的事情来找到在头文件中声明的实现。你需要告诉它。

你说得完全正确。我一直以为只要头文件和实现文件的名称相同,编译器在包含头文件时就会同时找到它们两个。 - Chris Cummings
这个关于为什么分离编译是一个特性的解释非常有用。谢谢。 - jds

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接