77

Win32コンソールアプリケーションにA.cppとB.cppの2つのファイルがあります。

2つのファイルには、次の2行のコードのみが含まれています。

#include "stdafx.h"
int k;

コンパイルするとエラーが発生します

Error   1   error LNK2005: "int k" (?a@@3HA) already defined in A.obj

何が起こっているのかわかりません。

誰かが私にこれを説明してもらえますか?

4

8 に答える 8

103

なぜこのエラーですか?

1つの定義規則に違反したため、リンクエラーが発生しました 。

推奨される解決策:


2つのcppファイルで同じ名前の変数が必要な場合は、エラーを回避するために名前のない名前空間(匿名の名前空間)を使用する必要があります。

namespace 
{
    int k;
}

複数のファイル間で同じ変数を共有する必要がある場合は、を使用する必要がありますextern

ああ

extern int k;

A.cpp

#include "A.h"
int k = 0;

B.cpp

#include "A.h"

//Use `k` anywhere in the file 
于 2012-04-06T16:50:03.753 に答える
83

プロジェクトの設定で /FORCE:MULTIPLE、リンカーのコマンドラインオプションに追加します。

MSDNから:「LINKがシンボルの複数の定義を検出するかどうかに関係なく、/ FORCE:MULTIPLEを使用して出力ファイルを作成します。」

于 2013-11-02T10:19:48.610 に答える
15

両方が同じ変数を参照するようにする場合は、一方にが、もう一方にint k;が必要です。extern int k;

この状況では、通常、定義(int k;)を1つの.cppファイルに入れ、宣言(extern int k;)をヘッダーに入れて、その変数にアクセスする必要がある場所に含めます。

kそれぞれがたまたま同じ名前を持つ別々の変数にしたい場合は、次staticのようにマークを付けることができますstatic int k;(すべてのファイル、または少なくとも1つのファイルを除くすべて)。または、匿名の名前空間を使用することもできます。

namespace { 
   int k;
};

繰り返しますが、多くても1つのファイルを除くすべてのファイルで。

Cでは、コンパイラーは一般的にこれについてそれほど気難しいものではありません。int k;具体的には、Cには「仮定義」の概念があるため、 (同じまたは別々のソースファイルに)2回のようなものがある場合、それぞれが仮定義として扱われ、それらの間に競合は発生しません。ただし、両方に初期化子を含む2つの定義を設定することはできないため、これは少し混乱する可能性があります。初期化子を含む定義は、暫定的な定義ではなく、常に完全な定義です。言い換えれば、int k = 1;2回出現することはエラーになりますがint k;、ある場所とint k = 1;別の場所ではエラーになりません。この場合、はint k;暫定的な定義として扱われ、は定義として扱われint k = 1;ます(両方とも同じ変数を参照します)。

于 2012-04-06T16:52:41.463 に答える
6

異なる.cppファイルで「k」を異なる値にしたい場合(したがって、2回宣言する)、両方のファイルを次のように変更してみてください。

namespace {
    int k;
}

これにより、名前「k」が翻訳単位全体で「k」を一意に識別することが保証されます。古いバージョンstatic int k;は非推奨です。

同じ値を指すようにする場合は、1つをに変更しextern int k;ます。

于 2012-04-06T16:50:30.610 に答える
6

どちらのファイルも、変数kを整数(int)として定義しています。

その結果、リンカは同じ名前の2つの変数を認識し、を参照する場合にどちらを使用すべきかがわかりませんk

これを修正するには、宣言の1つを次のように変更します。

extern int k;

つまり、「kは整数であり、ここで宣言されていますが、外部で定義されています(つまり、他のファイル)。」

kこれで、2つの異なるファイルから適切に参照できる変数が1つだけになりました。

于 2012-04-06T16:54:08.530 に答える
3

また、これらの変換ユニットでこの変数を共有する場合は、A.cppで定義int k;し、B.cppに配置extern int k;します。

于 2012-04-06T16:52:13.760 に答える
3

int k;ヘッダーファイルに存在すると、kこのヘッダーが含まれる各変換ユニット内でシンボルが定義されますが、リンカーはシンボルが1回だけ定義されることを想定しています(別名、単一定義規則違反)。

関与する提案externは間違いでexternはありませんが、C-ismであり、使用しないでください。

ODR違反を引き起こすことなくヘッダーファイルの変数を複数の変換単位で定義できるようにするC++17以前のソリューションは、テンプレートへの変換です。

template<typename x_Dummy = void> class
t_HeaderVariableHolder
{
    public: static int s_k;
};

template<typename x_Dummy> int t_HeaderVariableHolder<x_Dummy>::s_k{};

// Getter is necessary to decouple variable storage implementation details from access to it.
inline int & Get_K() noexcept
{
    return t_HeaderVariableHolder<>::s_k;
}

inlineC ++ 17では、変数を使用できるため、作業がはるかに簡単になります。

inline int g_k{};

// Getter is necessary to decouple variable storage implementation details from access to it.
inline int & Get_K() noexcept
{
    return g_k;
}
于 2019-04-12T20:59:40.310 に答える
1

リンカは、変数がk複数回定義されていることを通知します。実際、A.cppに定義があり、B.cppに別の定義があります。両方のコンパイルユニットは、リンカがプログラムの作成に使用する対応するオブジェクトファイルを生成します。問題は、あなたの場合、リンカーが使用する定義を知らないことkです。C ++では、同じ構成(変数、型、関数)の定義を1つだけ持つことができます。

それを修正するには、あなたの目標が何であるかを決定する必要があります

  • 両方とも名前が付けられた2つの変数が必要な場合はk、両方の.cppファイルで匿名の名前空間を使用して、k現在行っているように参照できます。

namespace {
  int k;
}
  • の1つを別の名前に変更kして、定義の重複を避けることができます。
  • k両方の.cppファイルで一度だけ定義して使用したい場合は、一方をとして宣言し、もう一方extern int k;はそのままにしておく必要があります。これにより、リンカはどちらの場合も1つの定義(変更されていないバージョン)を使用するようになります。これはextern、変数が別のコンパイル単位で定義されていることを意味します。
于 2012-04-06T16:54:46.213 に答える