休斯顿,我们有一个未定义的参考

Mat*_*lem 6 c++ linker undefined-reference

MEGAEDIT 3000

我发现了未定义引用的原因.我检查了我的.o文件中的符号,但它们只是丢失了.只是不在那里.没有明显的理由.我检查了妥协.o文件的来源,但一切似乎都没问题.我认为它可能是那些讨厌的#ifdef ... #endif块之一,狡猾地隐藏在代码的行中,像熊陷阱,等待它的受害者.但我找不到任何东西.将一些函数定义移动到文件的最后并将它们封装在#ifdef ... #endif适合Linux的块中之后,这些符号神奇地出现了,一切都很好.感谢您的时间.


过时了,不要读.

我最近被分配到一个大项目,我的工作是让它在Linux平台上运行.不幸的是,在我之前编写整个项目的程序员做了一个非常糟糕的工作,通过将文件B3D_Base.h(几乎每个标题都包含在内)创建#included到项目中的几乎每个头文件和源文件中创建了大量的循环依赖(创建了170多个文件#include "B3D_Base.h"线).这在Windows和Linux上编译期间没有引起任何问题.但是,这会在Linux上的链接阶段导致大量"未定义的引用"错误,即使所有目标文件和库都已链接.

ld在链接期间意识到通过所有目标文件的单次迭代的限制,我问:

有没有办法强制ld通过对象和库进行多次迭代,可能解决所有700多个未定义的引用错误,或者是我唯一的机会实际链接项目只是将所有#include "B3D_Base.h"行替换为正确的包含到所需的标题?

附录1:

这是一个我应该在Linux上运行的混乱的例子:

有一个头文件B3D_Base.h,向其中包括B3D_Loading3.h,含有类B3D_LOADING3.文件B3D_Loading3.cppB3D_Base.h包含和Base中未包含的内存管理器类.B3D_LOADING3 g_Loading3;声明一个实例B3D_Base.h并初始化+在项目的许多其他部分中使用.一切都编译得很好,但是当谈到链接时,我会遇到很多'undefined reference to g_Loading3'错误.还有更多这样的错误,抱怨对类实例和函数的未定义引用.这么干净的代码.

附录#2:

我将提供一个SSCCE示例,正如kfsone所要求的:

B3D_Base.h

... (many other includes)

#include "B3D_Loading3.h"
extern B3D_LOADING3 g_Loading3;

... (includes go on)
Run Code Online (Sandbox Code Playgroud)

B3D_Loading3.h/B3D_Loading.cpp

a full definition of class B3D_LOADING3, but no real declaration of g_Loading3
Run Code Online (Sandbox Code Playgroud)

AW_Game.cpp(使用g_Loading3的文件之一)

#include "B3D_Base.h"
... (some lines)
extern B3D_LOADING3 g_Loading3; 
Run Code Online (Sandbox Code Playgroud)

感谢kfsone让我彻底查看了代码,我设法找出问题所在:g_Loading3在任何地方定义为extern,因此没有正确定义.我只需extern要从一个文件中删除关键字.修正了我~30个链接错误.谢谢,kfsone.

附录#3:

但是对类函数的未定义引用仍然存在问题.经过一些代码扫描后,我认为由于过度使用#ifdef ... #endif子句(可移植性)甚至没有定义函数,因此可能是我的错,忽略了仅在某些被动ifdef块中定义的函数.我将尝试查看此问题,然后报告是否有任何更改.

附录#3报告:

即使在正确定义函数之后,undefined-reference-list也没有缩小.因此,问题仍有待讨论.

附录#3.1

我将提供一些示例代码.

foo.h中

#ifndef FOO_H
#define FOO_H

class Foo
{
    unsigned int    m_size;
    bool            m_lock;
    friend class ASDF;

public:
    unsigned int    m_id;

    void    Release() {delete this;}
    int     Lock(UINT OffsetToLock, UINT SizeToLock, VOID ** ppbData, DWORD Flags);
    int     Unlock();
};

// other class declarations to follow

#endif
Run Code Online (Sandbox Code Playgroud)

Foo.cpp中

#include "Foo.h"
// other includes and function definitions to follow

int Foo::Lock(UINT OffsetToLock, UINT SizeToLock, VOID ** ppbData, DWORD Flags)
{
    if(!m_lock)
    {
        // some locking code
    }
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

Baz.cpp

#include "Foo.h"  // although included, defined and declared, causes undef reference

Foo *m_pFoo;
// several lines later
m_pFoo->Lock(0,0,(void)&Asdf,NULL); // <-- compiles fine, causes undefined reference
Run Code Online (Sandbox Code Playgroud)

Nik*_* C. 2

值得检查的一件事是确定这是否是死代码剥离问题。因此,我过去曾遇到过“未定义的引用”链接器错误。

如果代码不被使用,MS 编译器将删除它。默认情况下,GCC 不会执行此操作,如果该代码使用未定义的符号,您将收到错误。由于 MS 编译器删除了这些调用,因此不会出现任何错误。为了查看这是否是您的问题,请将这些标志添加到您的编译标志中(对于 C++ 以及 C,如果您也有 C 源代码):

-fdata-sections -ffunction-sections

将此标志添加到您的链接标志:

-Wl,--gc-部分

(当然,请确保您使用的是g++链接,而不是ld。)

除此之外,我唯一的建议是使用正确的构建系统。我强烈推荐CMake。它简单、可靠,您应该能够快速学会它。为包含 100 多个源文件的 300k LOC 项目创建基于 CMake 的构建花了我不到 3 小时的时间(当时这是我第一次使用 CMake。)