繁体   English   中英

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

[英]Houston, we have an undefined reference

MEGAEDIT 3000

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


过时了,不要读。

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

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

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

附录1:

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

有一个头文件B3D_Base.h ,其中包含B3D_Loading3.h ,其中包含类B3D_LOADING3 文件B3D_Loading3.cpp仅包含B3D_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)

B3D_Loading3.h / B3D_Loading.cpp

a full definition of class B3D_LOADING3, but no real declaration of g_Loading3

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

#include "B3D_Base.h"
... (some lines)
extern B3D_LOADING3 g_Loading3; 

感谢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

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;
}

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

没有任何一个例子,它真的不清楚你的问题可能在哪里,除了它不是头文件的排序 - 它是那些头文件中的内容,以及随后链接模块的顺序。

GCC / LD只能抱怨被引用不只是申报的东西未定义的引用。

// t1.h
#pragma once
#include "t2.h"

class Foo1 {};
class Foo3 {}; // This is going to be our undef ref

extern Foo1 g_foo1_a;
extern Foo1& foo1_a();
extern Foo1 g_foo1_b;
extern Foo1& foo1_b();
extern Foo2 g_foo2;
extern Foo3 g_foo3;

// Uncomment this line to generate an undef ref
//static Foo3* g_foo3p = &g_foo3;


// t2.h
#pragma once
#include "t1.h"

class Foo2 {};

extern Foo2 g_foo2;

// t1.cpp
#include "t1.h"
#include "t2.h"

#if defined(_MSC_VER) && _MSC_VER > 9000
Foo3 g_foo3; // gcc won't produce one of these.
#endif

Foo1& foo1_b() { return g_foo1_b; }

int main(int argc, const char** argv)
{
}

// t2.cpp
#include "t1.h"

Foo1 g_foo1_b;

// Makefile
all:
    g++ -Wall -o t1.o -c t1.cpp
    g++ -Wall -o t2.o -c t2.cpp
    g++ -Wall -o t t1.o t2.o
    g++ -Wall -o t t2.o t1.o

在取消注释静态之前编译没有任何undef引用,此时你得到编译警告(未使用的变量)和未定义的引用警告,因为没有人实际实例化g_foo3。

所以 - 虽然我意识到你不能给我们你的确切代码,如果你想解决这个问题,你可能需要提出一个SSCCE 就像没有那样,在复制问题的过程中,你可能会想出来,这就是为什么通常要求在这里提问

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

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

-fdata-sections -ffunction-sections

这个标志链接标志:

-Wl,--gc-sections

(当然要确保你使用g++进行链接,而不是ld 。)

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

在Linux上,除非要学习深奥的ld参数,否则需要多个-l参数副本。 列出所有库三(是,3)次:

 -la -lb -lc -la -lb -lc -la -lb -lc

确保满足库之间的所有依赖关系。 请注意,数字3与库的数量无关。 某些版本的ld具有-load_all-(对此问题的解释较少)。

原因是链接器从右到左处理未定义的符号。 当它第一次看到-la ,它只从liba.a中获取基本exec所需的.o文件文件。 当它看到-lb它会加载base和liba所需的那些。 如果libb需要一些liba,那么除非你第二次列出liba等,否则你运气不好。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM