【问题标题】:C error: undefined reference to function, but it IS definedC 错误:未定义对函数的引用,但它已定义
【发布时间】:2021-06-08 17:16:46
【问题描述】:

只是一个简单的程序,但我不断收到此编译器错误。我正在使用 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: 对'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;
}

我不知道问题可能是什么。

【问题讨论】:

  • 你能发布你的makefile吗?此外,您定义了 2 个主要功能,这不太好。
  • 可能是 main() 的重新定义,它是您程序的入口点。摆脱point.c中的那个
  • @upswimsdn,是因为 main() 的双重定义吗?
  • 是的,这是我遇到的另一个问题,但主要问题是没有使用“gcc testpoint.c point.c”将两个文件编译在一起(请参阅接受的答案)。

标签: c function linker-errors undefined-reference


【解决方案1】:

我认为问题在于,当您尝试编译 testpoint.c 时,它包含 point.h,但不知道 point.c。由于point.c有create的定义,没有point.c会导致编译失败。

我不熟悉 MinGW,但是你需要告诉编译器去寻找 point.c。例如,使用 gcc 你可以这样做:

gcc point.c testpoint.c

正如others 所指出的,您还需要删除您的main 功能之一,因为您只能拥有一个。

【讨论】:

    【解决方案2】:

    你是如何进行编译和链接的?您需要指定这两个文件,例如:

    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 转关于优化)和$&lt; 是一个特殊的宏,可以扩展为源文件的名称。

    您通常将其存储在一个名为Makefile 的文件中,并且要构建您的程序,您只需在命令行中键入make。它隐式查找名为Makefile 的文件,并运行其中包含的任何规则。

    这样做的好处是make 会自动查看文件上的时间戳,因此它只会重新编译自上次编译以来发生更改的文件(即“.c " 文件的时间戳比匹配的 ".o" 文件更新)。

    还要注意 1) 在大型项目中使用 make 的方法有很多变化,2) 还有很多替代方法可以使用。我在这里只达到了最低限度的高点。

    【讨论】:

    • 这行得通,但这通常是如何将较大的 C 程序链接/编译在一起的吗?头文件中的 extern 关键字似乎没有解决任何问题。
    • 您的回答似乎只能解决提问者问题。这和回答问题不一样。例如,我有与标题完全相同的问题,但所有内容都在一个文件中。 stackoverflow 的目的是提供可搜索的答案,而不仅仅是帮助某人然后误导任何搜索相同问题的人。
    • @TimothySwan:我尝试提供相当笼统的答案,但是有一个限制。将每个答案都写成教科书对任何人都没有多大帮助 - 如果您处理的是单个文件,即使您的搜索结果出现了这个问题,这仍然是一个相当不同的问题。跨度>
    • @JerryCoffin 感谢您的回答!然而这又给我带来了另一个问题:当我们使用一些库函数如 printf 时,我们通常不会在编译期间将库文件添加到 gcc 命令中。我们只需要包含 stdio.h 就可以了。我描述的情况和问题中的情况有什么区别吗?
    • @ZiruiBai:唯一真正的区别是当gcc(或大多数其他编译器)生成链接器时,默认情况下它会告诉链接器链接到标准库。但是,对于任何其他库,这不会发生(而且通常甚至不会发生在标准库中的任何数学函数上——至少在大多数情况下,您需要显式链接它)。
    【解决方案3】:

    我最近遇到了这个问题。就我而言,我将 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 致敬,感谢他对effect of extern "C" 的清晰解释。)

    【讨论】:

      【解决方案4】:

      在point.h中的函数定义中添加“extern”关键字

      【讨论】:

      • extern 对函数没有任何影响(至少现在是这样),因为在标头中声明的每个函数都是公共/外部的。
      • 从函数定义中去掉“static”关键字
      猜你喜欢
      • 2022-05-03
      • 2017-06-29
      • 1970-01-01
      • 2011-05-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-11-01
      相关资源
      最近更新 更多