C错误:对函数的引用未定义,但它已被定义。

110

我只是写了一个简单的程序,但是编译时一直出现错误。我使用MinGW作为编译器。

这是头文件point.h的内容:

//type for a Cartesian point
typedef struct {
  double x;
  double y;
} Point;

Point create(double x, double y);
Point midpoint(Point p, Point q);

这里是 point.c

//This is the implementation of the point type
#include "point.h"

int main() {
  return 0;
}
Point create(double x, double y) {
  Point p;
  p.x = x;
  p.y = y;
  return p;
}

Point midpoint(Point p, Point q) {
  Point mid;
  mid.x = (p.x + q.x) / 2;
  mid.y = (p.y + q.y) / 2;
  return mid;
}

这里涉及到编译器问题。我一直收到以下错误提示:

testpoint.c: undefined reference to 'create(double x, double y)'

而它在point.c中已被定义。

这是一个名为testpoint.c的独立文件:

#include "point.h"
#include <assert.h>
#include <stdio.h>
int main() {
  double x = 1;
  double y = 1;
  Point p = create(x, y);

  assert(p.x == 1);
  return 0;
}
我不知道问题出在哪里。

9
你能贴出你的Makefile吗?另外,你定义了两个主函数,这可能不太好。 - George
可能是main()的重新定义,它是程序的入口点。摆脱point.c中的那个。 - RageD
@upswimsdn,是因为main()函数的重复定义吗? - Chan Kim
2
是的,那是我遇到的另一个问题,但主要问题是没有使用“gcc testpoint.c point.c”将两个文件一起编译(请参见被接受的答案)。 - upswimsdn
6个回答

149

你是如何进行编译和链接的?你需要指定两个文件,类似于:

gcc testpoint.c point.c

……这样,它才能知道将两个函数链接在一起。然而,按照当前的代码编写方式,您将遇到相反的问题:main 的多次定义。您需要/希望消除其中一个(无疑是 point.c 中的一个)。

在较大的程序中,通常会分别进行编译和链接,以避免重新编译任何未更改的内容。您通常通过 makefile 指定需要执行的操作,并使用 make 执行工作。在此情况下,您将拥有类似于以下内容:

OBJS=testpoint.o point.o

testpoint.exe: $(OBJS)
    gcc $(OJBS)

第一个只是一个对象文件名称的宏。您可以使用$(OBJS)展开它。第二个规则告诉make 1)可执行文件依赖于对象文件,并且2)当/如果与对象文件不同步时,告诉它如何创建可执行文件。

大多数make版本(包括我相当确定的MinGW中的版本)都有一个内置的"隐式规则",用于告诉它们如何从C源文件创建对象文件。通常看起来像这样:

.c.o:
    $(CC) -c $(CFLAGS) $<
假定C编译器的名称以名为CC的宏的形式存在(如CC = gcc)并允许您在名为CFLAGS的宏中指定任何您关心的标志(例如,CFLAGS = -O3 打开优化),其中$<是一个特殊的宏,展开为源文件的名称。您通常将此存储在名为Makefile的文件中,并在命令行上键入make来构建程序。它会隐式地查找名为Makefile的文件,并运行其中包含的所有规则。好处在于make会自动查看文件的时间戳,因此它只会重新编译自上次编译以来发生更改的文件(即“.c”文件的时间戳比相应的“.o”文件更新)。另外,请注意:1)在处理大型项目时使用make有许多变体,2)还有许多替代方案可用。我在这里只提及了最基本的要点。

2
这个可以工作,但这通常是如何将较大的C程序链接/编译在一起的吗?头文件中的extern关键字似乎没有解决任何问题。 - upswimsdn
1
似乎你只回答了足以解决提问者的问题,但这并不等同于回答问题。例如,我有与标题中完全相同的问题,但所有内容都在一个文件中。Stackoverflow 的目的是提供可搜索的答案,而不仅仅是帮助某人一次,然后误导任何搜索相同问题的人。 - Timothy Swan
4
@TimothySwan: 我尽量提供比较通用的答案,但是有些限制。把每个答案都变成教科书对大多数人并没有帮助——而且,如果你只处理一个文件,即使你的搜索结果包含了这个问题,它仍然是一个非常不同的问题。 - Jerry Coffin
@JerryCoffin 感谢您的回答!但是这给了我另一个问题:当我们使用一些库函数,比如printf时,通常在编译时不需要将库文件添加到gcc命令中。我们只需要包含stdio.h就可以了。那么我描述的情况和问题中的情况有什么区别吗? - Zirui Bai
1
@ZiruiBai:唯一真正的区别在于,当gcc(或大多数其他编译器)生成链接器时,默认情况下它会告诉链接器链接到标准库。但是,对于任何其他库,这种情况并不会发生(甚至在大多数情况下,标准库中的任何数学函数也不会自动链接--至少需要显式链接)。 - Jerry Coffin

34

最近我遇到了这个问题。在我的情况下,我的IDE设置为根据文件扩展名选择使用哪个编译器(C或C++),并且我正在尝试从C ++代码调用C函数(即来自.c文件)。

C函数的.h文件没有被包含在这种类型的保护中:

#ifdef __cplusplus
extern "C" {
#endif

// all of your legacy C code here

#ifdef __cplusplus
}
#endif

我本可以加上那部分内容,但我不想修改它,所以我就像这样将它包含在我的C++文件中:

extern "C" {
#include "legacy_C_header.h"
}

(感谢UncaAlby"extern "C""的作用进行清晰的解释。)


10
我认为问题在于当您尝试编译testpoint.c文件时,它包含了point.h文件,但不知道point.c文件的存在。由于point.c文件才定义了create函数,没有point.c文件将导致编译失败。
我不熟悉MinGW,但您需要告诉编译器查找point.c文件。例如,对于gcc编译器,您可以这样做:
gcc point.c testpoint.c

正如其他人所指出的那样,你还需要删除一个main函数,因为你只能有一个。


4
在point.h文件中的函数定义上添加"extern"关键字。

8
现在的情况是,函数声明头文件中的所有函数都是公共/外部函数,因此对函数使用extern关键字不会起到任何作用。 - Engineer
1
从函数定义中删除“static”关键字。 - endolith

0
我看到这里有一个问题在C编程语言中,我一直收到这个错误已经被回答了,所以这个线程似乎关闭了。但我不同意,因为这是不同的代码。
答案应该是我们不知道自定义头文件“functions.h”中有什么。同时,我们也不知道有什么。
MAPA m;
POSICAO heroi;

这些是函数还是常量? 如果这些是常量,那么应该在它们前面加上 #define,并且不加分号。

#define MAPA m 
#define POSICAO heroi

如果您打算对该函数进行原型设计,由于后面有分号,那么您没有插入括号()。 在这种情况下,MAPA和POSICAO是一些自定义类型的函数,其内容应在“Functions.h”中确定。

此外,您可能想要从其他目录导入函数、变量或常量,在这种情况下,您缺少了一个单词。

extern MAPA m;

-1

我遇到了类似的问题,需要在一个目录中运行一堆链接到一个带有自定义函数原型的头文件的.c文件。 我运行了:

$gcc -Wall -Werror -Wextra -pedantic -std=gnu89 *.c

出现以下错误:

/usr/bin/ld: /tmp/ccovH4zH.o: in function `_puts': 3-puts.c:(.text+0x2f): undefined reference to `_putchar'
/usr/bin/ld: 3-puts.c:(.text+0x51): undefined reference to `_putchar'
/usr/bin/ld: /tmp/ccGeWRqI.o: in function `main': _putchar.c:(.text+0xe): undefined reference to `_putchar'
/usr/bin/ld: _putchar.c:(.text+0x18): undefined reference to `_putchar'
/usr/bin/ld: _putchar.c:(.text+0x22): undefined reference to `_putchar'
/usr/bin/ld: /tmp/ccGeWRqI.o:_putchar.c:(.text+0x2c): more undefined references to `_putchar' follow
collect2: error: ld returned 1 exit status

注意:所有文件都链接到同一个带有所有函数声明的头文件。
我成功地添加了-c选项到gcc编译器后进行了编译,如下所示: $gcc -Wall -Werror -Wextra -pedantic -std=gnu89 -c *.c
这个运行成功了。
以防万一有人遇到相同的问题。

然而,这并没有解决问题。使用“-c”选项只是将C代码编译为目标代码,因为它们是相互依赖的,不能单独执行,如果要创建可执行文件,则需要将它们链接在一起,可能会再次出现相同的错误。 - Celuk

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