6

メガエディット 3000

未定義の参照の原因を発見しました。シンボルの .o ファイルをチェックしましたが、それらが欠落していました。そこにいなかっただけです。明らかな理由はありません。侵害した .o ファイルのソースを確認しましたが、すべて問題ないようでした。私は、これは厄介な#ifdef ... #endifブロックの 1 つで、クマのわなのようにコード行の間に巧妙に隠され、犠牲者を待っているのではないかと考えました。しかし、私は何も見つけることができませんでした。#ifdef ... #endif一部の関数定義をファイルの最後に移動し、それらを Linux に適したブロックで囲むと、シンボルが魔法のように表示され、すべて問題ありませんでした。お時間をいただきありがとうございます。


古いので読まないでください。

私は最近、1 つの大きなプロジェクトに割り当てられました。私の仕事は、Linux プラットフォームで実行することです。残念なことに、私の前にプロジェクト全体を書いたプログラマーは非常に貧弱な仕事をしておりB3D_Base.h、ほぼすべてのヘッダーが#includedである file を、プロジェクト内のほぼすべてのヘッダーとソース ファイルにインクルードすることで、大量の循環依存関係を作成しました (これにより、170 以上のファイルが#include "B3D_Base.h"ライン)。これにより、Windows と Linux の両方でコンパイル中に問題が発生することはありませんでした。ただし、これにより、すべてのオブジェクト ファイルとライブラリがリンクされているにもかかわらず、Linux でのリンク フェーズ中に多くの「未定義の参照」エラーが発生します。

リンク中のすべてのオブジェクトファイルの単一反復の制限を認識してld、私は尋ねています:

オブジェクトとライブラリを介して複数の反復を強制的ldに実行し、700 以上の未定義の参照エラーをすべて解決する方法はありますか、または#include "B3D_Base.h"必要なヘッダーへの適切なインクルードですべての行を置き換えるだけでプロジェクトを実際にリンクする唯一のチャンスですか?

付録1:

以下は、Linux で実行することになっている混乱の例です。

class を含む1 つのヘッダー ファイルB3D_Base.hがインクルードされます。ファイルはBase に含まれていないメモリ マネージャ クラスのみを含んでいます。インスタンスはで宣言され、プロジェクトの他の多くの部分で初期化+使用されます。すべて問題なくコンパイルできますが、リンクに関しては多くのエラーが発生します。そして、このようなエラーは他にもたくさんあり、クラス インスタンスと関数の両方への未定義の参照について不平を言っています。きれいなコードはこれで終わりです。B3D_Loading3.hB3D_LOADING3B3D_Loading3.cppB3D_Base.hB3D_LOADING3 g_Loading3;B3D_Base.h'undefined reference to g_Loading3'

付録 2:

kfsone が要求したように、SSCCE の例を提供します。

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 が使用されるファイルの 1 つ)

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

コードを徹底的に調べさせてくれた kfsone のおかげで、どこに問題があるのか​​を突き止めることができました。g_Loading3 はどこでも として定義されてexternいるため、適切に定義されていません。extern必要なのは、1 つのファイルからキーワードを削除することだけでした。~ 30 個のリンク エラーを修正しました。ありがとう、kfsone。

付録 3:

しかし、クラス関数への未定義の参照にはまだ問題があります。いくつかのコードをスキャンした後、#ifdef ... #endif句の過度の使用 (移植性) のために関数が定義されていないと思います。したがって、受動的な ifdef ブロックでのみ定義された関数を見落としているのは私のせいかもしれません。この問題を調べて、何か変更があれば後で報告します。

付録 3 レポート:

関数を適切に定義した後でも、undefined-reference-list は縮小しませんでした。したがって、この問題はまだ議論の余地があります。

付録 #3.1

いくつかのサンプルコードを提供します。

フー。

#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
4

3 に答える 3

2

チェックする価値のあることの 1 つは、それがデッド コード ストリッピングの問題であるかどうかを判断することです。このため、過去に「未定義の参照」リンカ エラーが発生しました。

MS コンパイラは、使用されていないコードを削除します。GCC はデフォルトではこれを行いません。そのコードが定義されていないシンボルを使用すると、エラーが発生します。MS コンパイラはこれらの呼び出しを削除するため、エラーは発生しません。これが問題かどうかを確認するには、次のフラグをコンパイル フラグに追加します (C ソースがある場合は、C++ と C の両方)。

-fdata-sections -ffunction-sections

そして、このフラグをリンク フラグに設定します。

-Wl,--gc-sections

(もちろんg++、 ではなく、リンクに使用していることを確認してくださいld。)

それ以外では、適切なビルド システムを使用することをお勧めします。CMakeを強くお勧めします。簡単で信頼性が高く、すぐに習得できるはずです。100 を超えるソース ファイルを含む 300k LOC プロジェクトの CMake ベースのビルドを作成するのに 3 時間もかかりませんでした (その時点で、CMake を使用したのは初めてでした)。

于 2013-07-22T11:38:00.893 に答える
2

例がなければ、ヘッダー ファイルの順序ではなく、それらのヘッダー ファイルの内容と、それに続いてモジュールをリンクする順序であることを除いて、問題がどこにあるのかは本当に不明です。

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

static のコメントを外すまで undef 参照なしでコンパイルします。その時点で、g_foo3 を実際にインスタンス化した人がいないため、コンパイル警告 (未使用の変数) と未定義参照警告の両方が表示されます。

そのため、正確なコードを提供できないことは承知していますが、問題を解決したい場合は、おそらくSSCCEを作成する必要があります。そうでない場合と同様に、問題を再現する過程で、おそらくそれを理解するでしょう。そのため、通常、ここで質問する必要があります

于 2013-07-20T19:55:15.070 に答える
1

Linux では、-l難解な ld 引数を学習したい場合を除き、引数の複数のコピーが必要になります。すべてのライブラリを 3 回 (はい、3 回) リストします。

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

ライブラリ間のすべての依存関係が満たされていることを確認します。3という数字は、ライブラリの数とは無関係であることに注意してください。の一部のバージョンにld-load_all-(この問題に対する解決策があります。

その理由は、リンカが未定義のシンボルを右から左に処理するためです。最初に見-laたとき、ベース exec に必要な liba.a から .o ファイルのみを取得します。ベースとliba-lbに必要なものをロードします。libb に liba の一部が必要な場合は、liba を 2 回リストアップしない限り、うまくいきません。

于 2013-07-20T19:12:25.560 に答える