代码之家  ›  专栏  ›  技术社区  ›  isomorphismes Marcin

无法使用GCC编译C++中的包含/库问题(Calpult2:LD返回1退出状态)

  •  4
  • isomorphismes Marcin  · 技术社区  · 14 年前

    我想打电话 ABC.cpp 需要什么 XYZ.h XYZ.cpp #include <XYZ.h> 以及 #include "XYZ.h"

    跑步 $ g++ -I. -l. ABC.cpp 在Ubuntu10终端给我:

    `/tmp/ccCneYzI.o: In function `ABC(double, double, unsigned long)':
    ABC.cpp:(.text+0x93): undefined reference to `GetOneGaussianByBoxMuller()'
    collect2: ld returned 1 exit status`
    

    以下是对ABC.cpp公司:

    #include "XYZ.h"
    #include <iostream>
    #include <cmath>
    
    using namespace std;
    
    double ABC(double X, double Y, unsigned long Z)
    {
    ...stuff...
    }
    
    int main()
    {
    ...cin, ABC(cin), return, cout...
    }
    

    这是XYZ.h:

    #ifndef XYZ_H
    #define XYZ_H
    
    double GetOneGaussianByBoxMuller();
    #endif
    

    这里是XYZ.cpp公司:

    #include "XYZ.h"
    #include <cstdlib>
    #include <cmath>
    
    // basic math functions are in std namespace but not in Visual C++ 6
    //(comment's in code but I'm using GNU, not Visual C++)
    
    #if !defined(_MSC_VER)
    using namespace std;
    #endif
    
    
    
    double GetOneGaussianByBoxMuller()
    {
    ...stuff...
    }
    

    我使用的是GNU编译器版本 g++ (Ubuntu 4.4.3-4ubuntu5) 4.4.3

    这是我的第一篇文章;我希望我包括了所有有人需要知道的帮助我的东西。我已经读过“相关问题”和 Gough article 列在其中一个响应中,并四处搜索错误消息。然而,我仍然不知道它如何适用于我的问题。

    提前谢谢!

    3 回复  |  直到 9 年前
        1
  •  9
  •   Hello71    13 年前

    g++ -I. -l. ABC.cpp 您要求编译器从中创建一个可执行文件 ABC.cpp . 但此文件中的代码对中定义的函数进行了回复 XYZ.cpp ,因此无法创建可执行文件,因为缺少该函数。

    你有两个选择(取决于你想做什么)。要么你一次给编译器所有的源文件,这样它就有了所有的定义。

     g++ -I. -l. ABC.cpp XYZ.cpp
    

    或者,你用 -c 选项编译为ABC.cpp公司目标代码(.obj在Windows中,在Linux中,.o)可以稍后链接,例如。

     g++ -I. -l. -c ABC.cpp
    

    这将产生 ABC.o 以后可以链接到 XYZ.o

    编辑:包含和链接有什么区别?

    预处理

    # 是一个 预处理器指令 在预处理阶段进行评估。这个 #include 复制粘贴。如果你写信 #include "XYZ.h" XYZ.h (包括对 XYZ.h公司 ).

    包含的目的是使声明可见。为了使用函数 GetOneGaussianByBoxMuller ,编译器需要知道 盖通高斯混合机

    编译

    这是编译器运行并将源代码转换为机器代码的部分。请注意,机器代码与 可执行文件 代码。可执行文件需要有关如何将机器代码和数据加载到内存中,以及如何在必要时引入外部动态库的附加信息。这不是在这里做的。这只是代码从C++到原始机器指令的部分。

    与java、python和其他一些语言不同,C++没有“模块”的概念。相反,C++是以 翻译单位 . 在几乎所有情况下,翻译单元都对应于单个(非头)源代码文件,例如。 XYZ.cpp公司 . 每个翻译单元都是独立编译的(无论您是单独运行 命令,或者一次将它们交给编译器)。

    编译源文件时,先运行预处理器,然后执行 #包括

    编译转换单元时,必须对使用的每个函数和每个变量 宣布 . 编译器将不允许您调用没有声明的函数或使用没有声明的全局变量,因为这样它就不知道所涉及的类型、参数、返回值等,并且无法生成合理的代码。这就是您需要头文件的原因——请记住,此时编译器甚至不知道是否存在任何其他源文件;它只考虑处理 #包括 指令。

    在编译器生成的机器代码中,没有变量名或函数名之类的东西。一切都必须成为一个内存地址。每个全局变量都必须转换为存储它的内存地址,每个函数都必须有一个内存地址,执行流在调用它时会跳转到这个地址。对于那些 定义 (即对于功能, 实施 )在翻译单元中,编译器可以分配一个地址。对于那些 宣布 (通常是包含头的结果)并且没有定义,此时编译器不知道内存地址应该是什么。这些函数和全局变量的编译器只有一个声明,而没有定义/实现 外部符号 ,并假定它们存在于不同的翻译单位中。目前,它们的内存地址用占位符表示。

    例如,编译 ABC ,因此它可以为函数分配一个地址 不管在哪个翻译单位 基础知识 盖通高斯混合机 未在该翻译单元中实现,因此其地址必须用占位符表示。

    编译翻译单元的结果是 对象文件 (与 .o 后缀)。

    决定 外部符号。也就是说,链接器查看一组对象文件,查看它们的外部符号是什么,然后尝试找出应该分配给它们的内存地址,替换占位符。

    在你的情况下函数 在对应于 XYZ.cpp公司 ,所以在里面 它被分配了一个特定的内存地址。在对应于 ABC.cpp公司 ,只是 宣布 ABC.o公司 XYZ.o.公司 我会看到的 ABC.o公司 盖通高斯混合机 ,在中找到地址 XYZ.o.公司 ABC.o公司

    如果链接器找不到 (在您的示例中,它只在 ABC.o公司 ,因为没有通过 XYZ.cpp公司 它将报告一个未解决的外部符号错误,也称为 符号未找到 .

    最后,一旦编译器解析了所有的外部符号,它就会合并所有现在没有占位符的对象代码,添加操作系统需要的所有加载信息,并生成一个可执行文件。塔达!

    注意,通过所有这些,文件名一点都不重要。它是一个 那个 XYZ.h公司 应该包含中定义的事物的声明 XYZ.cpp公司 全部的 给出的对象文件和 为尝试解析符号而提供的对象文件。它既不知道也不关心符号的声明在哪个头中,也不会尝试自动拉入其他对象文件或编译其他源文件以解析丢失的符号。

    ... 哇,太长了。

        2
  •  5
  •   Loki Astari    14 年前

    尝试

    g++ ABC.cpp XYZ.cpp
    

    如果要单独编译,则需要生成对象文件:

    g++ -c ABC.cpp
    g++ -c XYZ.cpp
    g++ ABC.o XYZ.o