Ошибка LNK2005, уже определена?
У меня есть 2 файла, A.cpp и B.cpp, в консольном приложении Win32.
Оба 2 файла содержат только следующие 2 строки кода:
#include "stdafx.h"
int k;
При компиляции он выдает ошибку
Error 1 error LNK2005: "int k" ([email protected]@3HA) already defined in A.obj
Я не понимаю, что происходит.
Может кто-нибудь объяснит мне это?
Ответы
Ответ 1
Почему эта ошибка?
Вы нарушили одно правило определения и, следовательно, ошибку связывания.
Предлагаемые решения:
Если вам нужна одинаковая именованная переменная в двух файлах cpp, вам необходимо использовать пространство имен Nameless (анонимное пространство имен), чтобы избежать ошибки.
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
Ответ 2
В настройках проекта добавьте /FORCE:MULTIPLE
к параметрам командной строки Linkers.
Из MSDN: "Используйте /FORCE: MULTIPLE для создания выходного файла независимо от того, найдет ли LINK несколько определений для символа".
Ответ 3
Если вы хотите, чтобы оба ссылались на одну и ту же переменную, один из них должен иметь int k;
, а другой должен иметь extern int k;
В этой ситуации вы обычно помещаете определение (int k;
) в один файл .cpp
и помещаете декларацию (extern int k;
) в заголовок, который должен быть включен везде, где вам нужен доступ к этой переменной.
Если вы хотите, чтобы каждая k
была отдельной переменной, которая имеет одно и то же имя, вы можете отметить ее как static
, например: static int k;
(во всех файлах или, по крайней мере, все, кроме одного файл). Кроме того, вы можете анонимное пространство имен:
namespace {
int k;
};
Опять же, во всех, но не более чем одном из файлов.
В C компилятор обычно не настолько разборчив. В частности, C имеет понятие "предварительное определение", поэтому, если у вас есть что-то вроде int k;
дважды (в одном и том же или отдельном исходном файле), каждый будет рассматриваться как предварительное определение, и конфликта не будет между ними. Это может быть немного запутанным, однако, поскольку у вас все еще нет двух определений, которые включают инициализаторы - определение с инициализатором всегда является полным определением, а не предварительным определением. Другими словами, int k = 1;
, появляющийся дважды, будет ошибкой, но int k;
в одном месте и int k = 1;
в другом случае не будет. В этом случае int k;
будет рассматриваться как предварительное определение и int k = 1;
как определение (и оба относятся к одной и той же переменной).
Ответ 4
Предполагая, что вы хотите, чтобы "k" было другим значением в разных файлах .cpp(следовательно, объявляя их дважды), попробуйте изменить оба файла на
namespace {
int k;
}
Это гарантирует, что имя "k" однозначно идентифицирует "k" для единиц перевода. Старая версия static int k;
устарела.
Если вы хотите, чтобы они указывали на одно и то же значение, измените его на extern int k;
.
Ответ 5
Оба файла определяют переменную k
как целое число (int
).
В результате компоновщик видит две переменные с тем же именем и не уверен, какой из них он должен использовать, если вы когда-либо ссылаетесь на k
.
Чтобы исправить это, измените одно из объявлений на:
extern int k;
Это означает: "k - целое число, объявленное здесь, но определенное извне (то есть другой файл)".
Теперь существует только одна переменная k
, которая может быть правильно упомянута двумя разными файлами.
Ответ 6
И если вы хотите, чтобы эти единицы перевода делили эту переменную, определите int k;
в A.cpp и поместите extern int k;
в B.cpp.
Ответ 7
Наличие int k;
в файле заголовка вызывает определение символа k
в каждой единице перевода, в которую включен этот заголовок, в то время как компоновщик ожидает, что он будет определен только один раз (так называемое нарушение правила определения).
Хотя внушение, связанное с extern
, не является неправильным, extern
является C-ism и не должен использоваться.
Решение до С++ 17, которое позволяло бы определять переменную в заголовочном файле в нескольких единицах перевода без нарушения ODR, было бы преобразованием в шаблон:
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;
}
С С++ 17 все становится намного проще, поскольку он позволяет inline
переменные:
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;
}
Ответ 8
Компонент сообщает, что вы указали переменную k
несколько раз. Действительно, у вас есть определение в A.cpp и другое в B.cpp. Оба блока компиляции создают соответствующий объектный файл, который использует компоновщик для создания вашей программы. Проблема в том, что в вашем случае компоновщик не знает, какое определение k
использовать. В С++ у вас может быть только одно определение одной и той же конструкции (переменная, тип, функция).
Чтобы исправить это, вам нужно решить, какова ваша цель
- Если вы хотите иметь две переменные, обе названные
k
, вы можете использовать анонимное пространство имен в обоих файлах .cpp, а затем обратиться к k
, как вы сейчас делаете:
.
namespace {
int k;
}
- Вы можете переименовать один из
k
в нечто другое, тем самым избегая дублирования исправления.
- Если вы хотите иметь только один раз определение
k
и использовать это в обоих файлах .cpp, вам нужно объявить в одном как extern int k;
и оставить его таким же, как и в другом. Это позволит компоновщику использовать одно определение (неизменная версия) в обоих случаях - extern
означает, что переменная определена в другом компиляторе.