정의되지 않은 참조/해결되지 않은 외부 기호 오류란 무엇이며 어떻게 수정해야 합니까?
정의되지 않은 참조/해결되지 않은 외부 기호 오류는 무엇입니까?일반적인 원인과 해결/예방 방법은 무엇입니까?
C++ 프로그램의 컴파일은 2.2(참고를 위해 Keith Thompson에 대한 참조)에 따라 여러 단계로 이루어집니다.
번역 구문 규칙 중 우선순위는 다음 단계로 지정됩니다(각주 참조).
- 물리 소스 파일 문자는 필요에 따라 구현 정의 방식으로 기본 소스 문자 세트에 매핑됩니다(엔드 오브 라인 표시기에 새 행 문자 도입).[SNIP]
- 백슬래시 문자(\) 직후의 각 인스턴스(\)를 삭제하고 물리 소스 라인을 스플라이싱하여 논리 소스 라인을 형성한다.[SNIP]
- 소스 파일은 전처리 토큰(2.5)과 공백 문자 시퀀스(댓글 포함)로 분해됩니다.[SNIP]
- 전처리 지시를 실행하고 매크로 호출을 확장하며 _Pragma 단항 연산자 식을 실행한다.[SNIP]
- 문자 리터럴 또는 문자열 리터럴 내의 각 소스 문자 세트멤버 및 문자 리터럴 또는 비원시 스트링 리터럴 내의 각 이스케이프 시퀀스 및 유니버설문자 이름은 실행 문자 세트의 대응하는 멤버로 변환됩니다.[ SNIP ]
- 인접한 문자열 리터럴 토큰이 연결됩니다.
- 토큰을 구분하는 공백 문자는 더 이상 의미가 없습니다.각 전처리 토큰은 토큰으로 변환된다(2.7).생성된 토큰은 구문 및 의미론적으로 분석되고 변환 단위로 변환됩니다.[SNIP]
- 번역 유닛과 인스턴스화 유닛은 다음과 같이 조합됩니다.[ SNIP ]
- 모든 외부 엔티티 참조가 해결됩니다. 라이브러리 구성 요소는 현재 변환에서 정의되지 않은 엔티티에 대한 외부 참조를 충족하기 위해 링크됩니다. 이러한 번역기 출력은 모두 실행 환경에서 실행에 필요한 정보를 포함한 프로그램 화상으로 수집된다.(내 것을 제외)
[각주] 실제로는 다른 단계가 서로 접혀질 수 있지만 구현은 이러한 개별 단계가 발생하는 것처럼 동작해야 합니다.
지정된 오류는 이 컴파일 마지막 단계(일반적으로 링크라고 함)에서 발생합니다.이는 기본적으로 다수의 구현 파일을 오브젝트 파일 또는 라이브러리로 컴파일한 후 이들 파일이 함께 작동하기를 원한다는 것을 의미합니다.
를 들어 했다고 칩시다.a
a.cpp
이제.b.cpp
그 기호를 선언하고 사용했어요링크하기 전에 단순히 그 기호가 어딘가에서 정의되었다고 가정할 뿐, 아직 어디에 있든 상관하지 않습니다.링크 단계는 기호를 찾아 올바르게 링크하는 역할을 합니다.b.cpp
(실제로 그것을 사용하는 오브젝트 또는 라이브러리로 이동합니다).
Studio 를 로 Microsoft Visual Studio 가 됩니다..lib
파일에는 내보낸 기호 테이블과 가져온 기호 테이블이 포함되어 있습니다.은 해당 됩니다..lib
(일부러)
다른 컴파일러/플랫폼에서도 동일한 메커니즘이 존재합니다.
는, 「에러 메시지」입니다.error LNK2001
,error LNK1120
,error LNK2019
Microsoft Visual Studio 및undefined reference to
GCC의 symbolName.
코드:
struct X
{
virtual void foo();
};
struct Y : X
{
void foo() {}
};
struct A
{
virtual ~A() = 0;
};
struct B: A
{
virtual ~B(){}
};
extern int x;
void foo();
int main()
{
x = 0;
foo();
Y y;
B b;
}
는 GCC에서 다음 오류를 생성합니다.
/home/AbiSfw/ccvvuHoX.o: In function `main':
prog.cpp:(.text+0x10): undefined reference to `x'
prog.cpp:(.text+0x19): undefined reference to `foo()'
prog.cpp:(.text+0x2d): undefined reference to `A::~A()'
/home/AbiSfw/ccvvuHoX.o: In function `B::~B()':
prog.cpp:(.text._ZN1BD1Ev[B::~B()]+0xb): undefined reference to `A::~A()'
/home/AbiSfw/ccvvuHoX.o: In function `B::~B()':
prog.cpp:(.text._ZN1BD0Ev[B::~B()]+0x12): undefined reference to `A::~A()'
/home/AbiSfw/ccvvuHoX.o:(.rodata._ZTI1Y[typeinfo for Y]+0x8): undefined reference to `typeinfo for X'
/home/AbiSfw/ccvvuHoX.o:(.rodata._ZTI1B[typeinfo for B]+0x8): undefined reference to `typeinfo for A'
collect2: ld returned 1 exit status
Microsoft Visual Studio에서도 같은 오류가 발생합니다.
1>test2.obj : error LNK2001: unresolved external symbol "void __cdecl foo(void)" (?foo@@YAXXZ)
1>test2.obj : error LNK2001: unresolved external symbol "int x" (?x@@3HA)
1>test2.obj : error LNK2001: unresolved external symbol "public: virtual __thiscall A::~A(void)" (??1A@@UAE@XZ)
1>test2.obj : error LNK2001: unresolved external symbol "public: virtual void __thiscall X::foo(void)" (?foo@X@@UAEXXZ)
1>...\test2.exe : fatal error LNK1120: 4 unresolved externals
일반적인 원인은 다음과 같습니다.
- 적절한 라이브러리/객체 파일에 대한 링크 또는 구현 파일 컴파일 실패
- 선언되고 정의되지 않은 변수 또는 함수입니다.
- 클래스 유형 멤버와 관련된 일반적인 문제
- 템플릿 구현이 표시되지 않습니다.
- C 프로그램에서 기호가 정의되어 C++ 코드로 사용되었습니다.
- 모듈/dll 간에 메서드/클래스를 잘못 Import/export하고 있습니다.(MSVS 고유)
- 순환 라이브러리 종속성
- 'WinMain@16'에 대한 정의되지 않은 참조
- 상호의존 라이브러리 순서
- 동일한 이름의 여러 원본 파일
- .lib 확장자를 할 때 하지 않음
#pragma
(Microsoft Visual Studio) - 템플릿 친구 문제
- 이 없다
UNICODE
(Definitions) - const 변수 선언/정의에 "extern"이 없습니다(C++만 해당).
- 다중 파일 프로젝트에 대해 Visual Studio 코드가 구성되지 않았습니다.
클래스 멤버:
한 수 a.virtual
.destructor가 합니다.
소멸자를 pure로 선언하는 경우에도 일반 함수와 달리 이를 정의해야 합니다.
struct X
{
virtual ~X() = 0;
};
struct Y : X
{
~Y() {}
};
int main()
{
Y y;
}
//X::~X(){} //uncomment this line for successful definition
이는 개체가 암묵적으로 파괴될 때 기본 클래스 소멸자가 호출되기 때문에 정의가 필요하기 때문입니다.
virtual
메서드는 구현하거나 순수하게 정의해야 합니다.
은 논투 논투 논투 논투 논투 논투 논투 논투 논투 논투 논다와 .virtual
츠미야순수 선언에 의해 더미의 vtable이 생성되어 함수를 사용하지 않으면 링커 오류가 발생할 수 있습니다.
struct X
{
virtual void foo();
};
struct Y : X
{
void foo() {}
};
int main()
{
Y y; //linker error although there was no call to X::foo
}
동작시키려면 , 「」를 합니다.X::foo()
★★★★★★★★★★★★★★★★★★:
struct X
{
virtual void foo() = 0;
};
(非)virtual
멤버
일부 멤버는 명시적으로 사용하지 않더라도 정의해야 합니다.
struct A
{
~A();
};
다음과 같은 경우 오류가 발생합니다.
A a; //destructor undefined
실장은 클래스 정의 자체에서 인라인화할 수 있습니다.
struct A
{
~A() {}
};
또는 외부:
A::~A() {}
는 " "로 마킹해야 합니다."로.inline
다중 정의를 방지합니다.
사용되는 모든 멤버 메서드를 정의해야 합니다.
일반적인 실수는 이름 수식을 잊어버리는 것입니다.
struct A
{
void foo();
};
void foo() {}
int main()
{
A a;
a.foo();
}
정의는 다음과 같습니다.
void A::foo() {}
static
데이터 멤버는 클래스 외부에서 단일 변환 단위로 정의해야 합니다.
struct X
{
static int x;
};
int main()
{
int x = X::x;
}
//int X::x; //uncomment this line to define X::x
는 에이셜라 an a a a a a a a a a a a a a a a a a a a a a에 할 수 있다.static
const
클래스 정의 내의 정수형 또는 열거형 데이터 멤버. 단, 이 멤버를 사용하려면 위에서 설명한 네임스페이스 범위 정의가 필요합니다.C에서는, 모든 C++11 의 에서 초기화가 합니다.static const
데이터 멤버
적절한 라이브러리/객체 파일에 대한 링크 또는 구현 파일 컴파일 실패
일반적으로 각 변환 유닛은 해당 변환 유닛에 정의된 심볼의 정의를 포함하는 오브젝트 파일을 생성합니다.이러한 기호를 사용하려면 해당 객체 파일에 대해 링크해야 합니다.
gcc에서 명령줄에서 함께 링크할 모든 개체 파일을 지정하거나 구현 파일을 함께 컴파일합니다.
g++ -o test objectFile1.o objectFile2.o -lLibraryName
-l...
이든 .o
/.c
/.cpp
files.complete files files files files files files files files.
libraryName
플랫폼 고유의 추가는 하지 않고 라이브러리의 이름만 나타냅니다.들어 "Linux"라고 .libfoo.so
'아까라고밖에 안 써있지 않아요?-lfoo
에서는, 파일을 라고 하는 경우가 .foo.lib
이치노 수 는, 「」를 사용해 가 있는 경우가 .-L‹directory›
마세요.-l
★★★★★★★★★★★★★★★★★」-L
.
XCode의 경우: 사용자 헤더 검색 경로 추가 -> 라이브러리 검색 경로 추가 -> 실제 라이브러리 참조를 프로젝트 폴더에 드래그 앤 드롭합니다.
MSVS에서 프로젝트에 추가된 파일은 자동으로 오브젝트 파일을 링크하여lib
이치노다른 '부호하다'를.lib
파일을 표시합니다. 작업은 인 "링커"에서합니다.Input -> Additional Dependencies
(로의 패스)lib
해야 합니다.Linker -> General -> Additional Library Directories
에서 lib
이치노
컴파일에 파일을 추가하는 것을 잊은 경우도 있습니다.이 경우 오브젝트 파일이 생성되지 않습니다.gcc에서는 명령행에 파일을 추가합니다.MSVS에서는 프로젝트에 파일을 추가하면 자동으로 컴파일됩니다(단, 수동으로 파일을 빌드로부터 개별적으로 제외할 수도 있습니다).
하지 않은 되지 않은 입니다.__imp_
매뉴얼에서 함수 이름을 검색하면 사용할 라이브러리가 나타납니다.예를 들어 MSDN은 "라이브러리"라는 섹션의 각 기능 하단의 상자에 정보를 넣습니다.
선언되었지만 변수 또는 함수를 정의하지 않았습니다.
일반적인 변수 선언은 다음과 같습니다.
extern int x;
이것은 선언일 뿐이므로 하나의 정의가 필요합니다.해당하는 정의는 다음과 같습니다.
int x;
예를 들어 다음과 같은 경우 오류가 발생합니다.
extern int x;
int main()
{
x = 0;
}
//int x; // uncomment this line for successful definition
기능에도 같은 발언이 적용됩니다.함수를 정의하지 않고 선언하면 다음 오류가 발생합니다.
void foo(); // declaration only
int main()
{
foo();
}
//void foo() {} //uncomment this line for successful definition
실장하는 함수가 선언한 함수와 정확히 일치하는지 주의해 주세요.예를 들어, 일치하지 않는 cv-qualifier가 있을 수 있습니다.
void foo(int& x);
int main()
{
int x;
foo(x);
}
void foo(const int& x) {} //different function, doesn't provide a definition
//for void foo(int& x)
미스매치의 다른 예는 다음과 같습니다.
- 한 네임스페이스에 선언된 함수/변수, 다른 네임스페이스에 정의된 값입니다.
- 클래스 멤버로 선언된 함수/변수, 글로벌로 정의됨(또는 그 반대).
- 함수 반환 유형, 매개 변수 번호와 유형 및 호출 규칙이 모두 일치하지 않습니다.
컴파일러로부터의 에러 메세지는, 선언되었지만 정의되어 있지 않은 변수나 함수의 완전한 선언을 제공하는 경우가 많습니다.제공한 정의와 자세히 비교합니다.모든 세부 사항이 일치하는지 확인하십시오.
상호의존 링크 라이브러리가 지정된 순서가 잘못되었습니다.
라이브러리가 서로 의존하는 경우 라이브러리의 링크 순서는 중요합니다.라이브러리의 A
에 B
, , , 「 」libA
앞에 표시해야 합니다.libB
링커 플래그를 설정합니다.
예를 들어 다음과 같습니다.
// B.h
#ifndef B_H
#define B_H
struct B {
B(int);
int x;
};
#endif
// B.cpp
#include "B.h"
B::B(int xx) : x(xx) {}
// A.h
#include "B.h"
struct A {
A(int x);
B b;
};
// A.cpp
#include "A.h"
A::A(int x) : b(x) {}
// main.cpp
#include "A.h"
int main() {
A a(5);
return 0;
};
라이브러리를 만듭니다.
$ g++ -c A.cpp
$ g++ -c B.cpp
$ ar rvs libA.a A.o
ar: creating libA.a
a - A.o
$ ar rvs libB.a B.o
ar: creating libB.a
a - B.o
컴파일:
$ g++ main.cpp -L. -lB -lA
./libA.a(A.o): In function `A::A(int)':
A.cpp:(.text+0x1c): undefined reference to `B::B(int)'
collect2: error: ld returned 1 exit status
$ g++ main.cpp -L. -lA -lB
$ ./a.out
다시 한 번 말하지만 순서는 중요합니다!
"표준 참조/분해된 외부 기호"란 무엇입니까?
정의되지 않은 참조/해결되지 않은 외부 기호가 무엇인지 설명하겠습니다.
주의: 저는 g++와 Linux를 사용하고 있으며, 모든 예시는 이에 대한 것입니다.
예를 들어 몇 가지 코드가 있습니다.
// src1.cpp
void print();
static int local_var_name; // 'static' makes variable not visible for other modules
int global_var_name = 123;
int main()
{
print();
return 0;
}
그리고.
// src2.cpp
extern "C" int printf (const char*, ...);
extern int global_var_name;
//extern int local_var_name;
void print ()
{
// printf("%d%d\n", global_var_name, local_var_name);
printf("%d\n", global_var_name);
}
객체 파일 만들기
$ g++ -c src1.cpp -o src1.o
$ g++ -c src2.cpp -o src2.o
어셈블러 단계가 끝나면 내보낼 기호가 포함된 객체 파일이 생성됩니다.기호를 보세요.
$ readelf --symbols src1.o
Num: Value Size Type Bind Vis Ndx Name
5: 0000000000000000 4 OBJECT LOCAL DEFAULT 4 _ZL14local_var_name # [1]
9: 0000000000000000 4 OBJECT GLOBAL DEFAULT 3 global_var_name # [2]
출력에서 일부 행을 거부했습니다. 문제가 되지 않기 때문입니다.
따라서 내보낼 기호를 볼 수 있습니다.
[1] - this is our static (local) variable (important - Bind has a type "LOCAL")
[2] - this is our global variable
src2.cpp는 아무것도 내보내지 않으며 그 기호는 표시되지 않습니다.
오브젝트 파일 링크
$ g++ src1.o src2.o -o prog
실행하다
$ ./prog
123
링커는 내보낸 기호를 보고 링크합니다.여기와 같이 src2.cpp 행의 코멘트를 해제하려고 합니다.
// src2.cpp
extern "C" int printf (const char*, ...);
extern int global_var_name;
extern int local_var_name;
void print ()
{
printf("%d%d\n", global_var_name, local_var_name);
}
오브젝트 파일을 재구축합니다.
$ g++ -c src2.cpp -o src2.o
OK(오류 없음)는 오브젝트 파일만 빌드하기 때문에 링크는 아직 이루어지지 않았습니다.링크 시도
$ g++ src1.o src2.o -o prog
src2.o: In function `print()':
src2.cpp:(.text+0x6): undefined reference to `local_var_name'
collect2: error: ld returned 1 exit status
이 문제는 local_var_name이 정적이기 때문에 발생합니다.즉, 다른 모듈에서는 표시되지 않습니다.이제 좀 더 깊게.번역 단계 출력을 가져옵니다.
$ g++ -S src1.cpp -o src1.s
// src1.s
look src1.s
.file "src1.cpp"
.local _ZL14local_var_name
.comm _ZL14local_var_name,4,4
.globl global_var_name
.data
.align 4
.type global_var_name, @object
.size global_var_name, 4
global_var_name:
.long 123
.text
.globl main
.type main, @function
main:
; assembler code, not interesting for us
.LFE0:
.size main, .-main
.ident "GCC: (Ubuntu 4.8.2-19ubuntu1) 4.8.2"
.section .note.GNU-stack,"",@progbits
local_var_name에는 라벨이 없기 때문에 링커가 찾을 수 없습니다.하지만 우리는 해커입니다:) 그리고 우리는 그것을 고칠 수 있습니다.텍스트 편집기에서 src1.s를 열고 변경합니다.
.local _ZL14local_var_name
.comm _ZL14local_var_name,4,4
로.
.globl local_var_name
.data
.align 4
.type local_var_name, @object
.size local_var_name, 4
local_var_name:
.long 456789
즉, 다음과 같이 해야 합니다.
.file "src1.cpp"
.globl local_var_name
.data
.align 4
.type local_var_name, @object
.size local_var_name, 4
local_var_name:
.long 456789
.globl global_var_name
.align 4
.type global_var_name, @object
.size global_var_name, 4
global_var_name:
.long 123
.text
.globl main
.type main, @function
main:
; ...
local_var_name의 가시성을 변경하고 값을 456789로 설정했습니다.이 파일에서 개체 파일 구축 시도
$ g++ -c src1.s -o src2.o
ok, readelf 출력 참조(표준)
$ readelf --symbols src1.o
8: 0000000000000000 4 OBJECT GLOBAL DEFAULT 3 local_var_name
local_var_name에는 Bind GLOBAL(로컬)이 있습니다.
링크
$ g++ src1.o src2.o -o prog
실행하다
$ ./prog
123456789
네, 해킹합니다:)
따라서 링커가 개체 파일에서 전역 기호를 찾을 수 없는 경우 "정의되지 않은 참조/해결되지 않은 외부 기호 오류"가 발생합니다.
C 프로그램에서 기호가 정의되어 C++ 코드로 사용되었습니다.
변수) " " ( " " )void foo()
C 프로그램에서 정의되어 C++ 프로그램에서 사용하려고 합니다.
void foo();
int main()
{
foo();
}
C++ 링커는 이름이 망가질 것으로 예상하기 때문에 함수를 다음과 같이 선언해야 합니다.
extern "C" void foo();
int main()
{
foo();
}
변수)는 C 됩니다.void foo()
C++:
extern "C" void foo();
C++ 링크로 C++ 프로그램에서 사용하려고 합니다.
라이브러리 전체가 헤더 파일에 포함되어 있는 경우(및 C 코드로 컴파일된 경우), include는 다음과 같아야 합니다.
extern "C" {
#include "cheader.h"
}
다른 모든 것이 실패하면 다시 컴파일합니다.
최근 Visual Studio 2012에서 해결되지 않은 외부 오류를 문제 파일을 다시 컴파일하는 것만으로 제거할 수 있었습니다.다시 만들었을 때 오류가 사라졌어요.
이 문제는 보통 두 개 이상의 라이브러리가 순환 종속성을 가질 때 발생합니다.라이브러리 A는 B.lib의 기호를 사용하고 라이브러리 B는 A.lib의 기호를 사용하려고 합니다.둘 다 처음부터 존재하지 않는다.A를 컴파일하려고 하면 링크 스텝은 B.lib을 찾을 수 없기 때문에 실패합니다.A.lib은 생성되지만 dll은 생성되지 않습니다.그런 다음 B를 컴파일하면 성공하고 B.lib가 생성됩니다.이것으로 B.lib가 발견되었기 때문에 A를 다시 컴파일 할 수 있습니다.
템플릿 구현이 표시되지 않습니다.
전문화되지 않은 템플릿은 해당 템플릿의 정의를 사용하는 모든 변환 단위에 표시해야 합니다., 파일로 수 .실장을 분리할 필요가 있는 경우는, 통상의 회피책은, 다음과 같습니다.impl
템플릿을 선언하는 헤더 끝에 포함하는 파일입니다.일반적인 상황은 다음과 같습니다.
template<class T>
struct X
{
void foo();
};
int main()
{
X<int> x;
x.foo();
}
//differentImplementationFile.cpp
template<class T>
void X<T>::foo()
{
}
를 해결하려면 의 .X::foo
헤더 파일 또는 헤더 파일을 사용하는 변환 유닛에서 볼 수 있는 곳으로 이동합니다.
전용 템플릿은 구현 파일에 구현할 수 있으며 구현 내용을 표시할 필요는 없지만 전문화는 사전에 선언해야 합니다.
자세한 설명과 다른 가능한 해결책(명시적 인스턴스화)에 대해서는 다음 질문과 답변을 참조하십시오.
이것은 모든 VC++ 프로그래머가 여러 번 본 가장 혼란스러운 오류 메시지 중 하나입니다.먼저 명확히 합시다.
A. 기호란 무엇입니까?간단히 말해서, 기호는 이름입니다.변수 이름, 함수 이름, 클래스 이름, typedef 이름 또는 C++ 언어에 속하는 이름 및 기호를 제외한 모든 이름을 사용할 수 있습니다.이는 사용자 정의 또는 의존관계 라이브러리(다른 사용자 정의)에 의해 도입됩니다.
B. 외부란 무엇입니까?VC++에서는 모든 소스 파일(.cpp,.c 등)이 번역 유닛으로 간주되어 컴파일러는 한 번에 1개의 유닛을 컴파일하고 현재 변환 유닛의 오브젝트 파일(.obj)을 생성합니다.(이 소스 파일에 포함된 모든 헤더 파일은 사전 처리되며 이 변환 유닛의 일부로 간주됩니다.)번역 유닛 내의 모든 것은 내부로 간주되며, 그 이외의 모든 것은 외부로 간주됩니다.C++에서는 다음과 같은 키워드를 사용하여 외부 기호를 참조할 수 있습니다.extern
,__declspec (dllimport)
기타 등등.
C. 해결이란 무엇입니까?해결은 연결 시간 용어입니다.링크 시간 동안 링커는 내부적으로 정의를 찾을 수 없는 개체 파일의 모든 심볼에 대한 외부 정의를 찾으려고 시도합니다.이 검색 프로세스의 범위는 다음과 같습니다.
- 컴파일 시간에 생성된 모든 개체 파일
- 이 빌딩 애플리케이션의 추가 종속성으로 명시적으로 또는 암시적으로 지정된 모든 라이브러리(.lib).
이 검색 프로세스를 해결이라고 합니다.
D. 마지막으로, 왜 해결되지 않은 외부 기호인가?링커는 내부적으로 정의가 없는 심볼의 외부 정의를 찾을 수 없는 경우 Unresolved External Symbol 오류를 보고합니다.
E. LNK2019: Unresolved External Symbol 오류의 가능한 원인입니다.이 에러는 링커가 외부 심볼의 정의를 찾지 못했기 때문에 발생할 수 있는 원인을 다음과 같이 분류할 수 있습니다.
- 정의가 존재합니다.
예를 들어 a.cpp에 정의된 foo라는 함수가 있는 경우:
int foo()
{
return 0;
}
b.cpp에서는 함수 foo를 호출하고 싶기 때문에
void foo();
하려면 foo foo로 발음합니다.bar()
:
void bar()
{
foo();
}
이 코드를 빌드하면 foo가 해결되지 않은 기호임을 나타내는 LNK2019 오류가 나타납니다.이 경우 foo()의 정의가 a.cpp로 되어 있지만 호출하는 것과 다른(다른 반환값) 것을 알 수 있습니다.정의가 존재하는 경우입니다.
- 정의가 존재하지 않습니다.
(「」에서).Project | Properties | Configuration Properties | Linker | Input | Additional Dependency
를 참조해 주세요를 참조해 주세요.현재 검색 범위에 정의가 존재하지 않으므로 링커는 LNK2019를 보고합니다.
모듈/dll 간에 메서드/클래스의 Import/export가 잘못되어 있다(컴파일러 고유).
에서는 MSVS를 사용하여 내보내기 및 .__declspec(dllexport)
★★★★★★★★★★★★★★★★★」__declspec(dllimport)
.
이 이중 기능은 보통 매크로를 사용하여 얻을 수 있습니다.
#ifdef THIS_MODULE
#define DLLIMPEXP __declspec(dllexport)
#else
#define DLLIMPEXP __declspec(dllimport)
#endif
「」THIS_MODULE
는 함수를 내보내는 모듈에서만 정의됩니다.이렇게 하면 선언문은 다음과 같습니다.
DLLIMPEXP void foo();
까지 확장하다.
__declspec(dllexport) void foo();
현재 모듈에 정의가 포함되어 있으므로 함수를 내보내도록 컴파일러에 지시합니다.선언을 다른 모듈에 포함할 경우 선언은 로 확장됩니다.
__declspec(dllimport) void foo();
는 컴파일러에 대해 링크한 라이브러리 중 하나에 정의가 있음을 알려줍니다(1 참조).
클래스 Import/export는 다음과 같이 할 수 있습니다.
class DLLIMPEXP X
{
};
정의되지 않은 참조 또는 유사한 '유사' 진입점 참조(특히 시각적 참조)
실제 IDE에서 올바른 프로젝트 유형을 선택하지 못했을 수 있습니다.IDE를 사용합니다.Windows 되는 Windows가 (위의된) 합니다.int main(int argc, char** argv);
★★★★★★ 。
IDE에서 일반 콘솔 프로젝트를 지원하는 경우 윈도우즈 응용 프로그램 프로젝트 대신 이 프로젝트 유형을 선택할 수 있습니다.
다음은 실제 문제에서 더 자세히 처리한 사례 1과 사례 2입니다.
또한 서드파티 라이브러리를 사용하는 경우 올바른 32/64비트 바이너리가 있는지 확인하십시오.
는 Microsoft®를 하고 있습니다.#pragma
링크 시 올바른 라이브러리를 참조한다.
#pragma comment(lib, "libname.lib")
라이브러리의 디렉토리를 포함한 라이브러리 경로와 더불어 라이브러리의 전체 이름이 되어야 합니다.
Visual Studio NuGet 패키지를 새 도구 세트 버전으로 업데이트해야 합니다.
libpng를 Visual Studio 2013과 연결하려다 문제가 발생했습니다.문제는 패키지 파일에 Visual Studio 2010 및 2012용 라이브러리만 포함되어 있다는 것입니다.
올바른 해결책은 개발자가 업데이트된 패키지를 출시하고 업그레이드하기를 바라는 것이지만, VS2013의 추가 설정에서 VS2012 라이브러리 파일을 가리키며 해킹함으로써 효과가 있었습니다.
그 를 편집했다(<일부러>에서 했다.packages
합니다.packagename\build\native\packagename.targets
에 있는 모든 , , 모든 복사,v110
를 참조해 주세요.는 변했 the the the the를 .v110
로로 합니다.v120
파일명 패스를 모두 로서 남겨두기 위해서만 주의해 주세요.v110
이를 통해 Visual Studio 2013은 2012년 라이브러리에 링크할 수 있었고, 이 경우에는 작동했습니다.
1,000개의 .cpp 파일과 1,000개의 .h 파일이 있는 c++로 작성된 큰 프로젝트가 있다고 가정합니다.이 프로젝트도 10개의 정적 라이브러리에 의존합니다.Windows 를 사용하고, Visual Studio 20xx 로 프로젝트를 빌드한다고 합시다.Ctrl + F7 Visual Studio 키를 눌러 솔루션 전체를 컴파일할 때 (솔루션 내에 프로젝트가1개밖에 없다고 가정합니다)
컴필레이션의 의미는 무엇입니까?
- Visual Studio에서 .vcxproj 파일을 검색하고 확장자가 .cpp인 각 파일의 컴파일을 시작합니다.컴파일 순서가 정의되지 않았습니다.따라서 main.cpp 파일이 먼저 컴파일되어 있다고 가정해서는 안 됩니다.
- .cpp 파일이 .cpp 파일에 정의되어 있거나 정의되어 있지 않은 기호를 찾기 위해 추가 .h 파일에 의존하고 있는 경우.
- 컴파일러가 하나의 기호를 찾을 수 없는 .cpp 파일이 존재하는 경우 컴파일러 시간 오류로 인해 "심볼 x를 찾을 수 없습니다"라는 메시지가 나타납니다.
- 확장자가 .cpp인 각 파일에 대해 개체 파일 .o가 생성되고 Visual Studio도 ProjectName이라는 파일에 출력을 씁니다.Cpp. 청소.링커에 의해 처리되어야 하는 모든 오브젝트파일을 포함하는 txt.
컴파일의 두 번째 단계는 Linker에 의해 수행됩니다.링커는 모든 오브젝트 파일을 Marge하고 최종적으로 출력(실행 가능 파일 또는 라이브러리)을 빌드해야 합니다.
프로젝트 링크 단계
- 모든 오브젝트 파일을 해석하여 헤더로만 선언된 정의를 찾습니다(예: 이전 답변에서 설명한 클래스의 한 메서드 코드 또는 클래스 내의 멤버인 정적 변수의 초기화가 이벤트).
- 개체 파일에서 하나의 기호를 찾을 수 없는 경우 추가 라이브러리에서도 검색됩니다.새 라이브러리를 프로젝트 구성 속성 -> VC+ 디렉터리에 대한 새 라이브러리를 추가할 수 있는 라이브러리 및 구성 폴더를 찾을 수 있습니다. 링크 시간e와 같이 들리는 오류
error LNK2001: unresolved external symbol "void __cdecl foo(void)" (?foo@@YAXXZ)
관찰
- 링커가 다른 라이브러리에서 검색하지 않는 기호를 발견하면
- 라이브러리의 링크 순서가 중요합니다.
- Linker는 하나의 정적 라이브러리에서 외부 기호를 찾으면 프로젝트 출력에 기호를 포함합니다.다만, 라이브러리가 공유되고 있는 경우( dynamic ) 출력에 코드(기호)는 포함되지 않지만 런타임 크래시가 발생할 수 있습니다.
이런 종류의 오류를 해결하는 방법
컴파일러 시간 오류:
- 반드시 c++ 프로젝트의 구문을 올바르게 기입해 주세요.
링커 시간 오류
- 헤더 파일에 선언하는 모든 기호를 정의합니다.
#pragma once
할 수 .- 외부 라이브러리에 헤더 파일에서 정의한 다른 기호와 충돌할 수 있는 기호가 포함되어 있지 않은지 확인하십시오.
- 템플릿을 사용하여 헤더 파일에 각 템플릿 함수의 정의를 포함시켜 컴파일러가 인스턴스화에 적합한 코드를 생성할 수 있도록 하는 경우.
링커를 사용하여 오류를 진단합니다.
대부분의 최신 링커에는 다양한 정도로 출력되는 상세 옵션이 포함되어 있습니다.
- 링크 호출(명령줄),
- 링크 스테이지에 포함되는 라이브러리의 데이터
- 도서관의 위치,
- 사용되는 검색 경로.
gcc를 clang으로 .-v -Wl,--verbose
★★★★★★★★★★★★★★★★★」-v -Wl,-v
명령행으로 이동합니다.한 것에 대하여는 여기를 해 주세요.
- Linux ldman 페이지
- LLVM 링커페이지
- "GCC의 개요" 9장.
경우 MSVC의 경우/VERBOSE
)/VERBOSE:LIB
가 링크 명령줄에 추가됩니다.
- 링커 옵션의 [MSDN]페이지
컴파일러/IDE 버그
최근 이 문제가 발생했는데, Visual Studio Express 2013에서 버그가 발생했습니다.버그를 극복하기 위해 프로젝트에서 소스 파일을 삭제하고 다시 추가해야 했습니다.
컴파일러/IDE의 버그일 가능성이 있다고 생각되는 경우는, 다음의 순서에 따라 주세요.
- 프로젝트를 청소합니다(일부 IDE에는 이 작업을 수행할 수 있는 옵션이 있으며 개체 파일을 삭제하여 수동으로 수행할 수도 있습니다).
- 원본에서 모든 소스 코드를 복사하여 새 프로젝트를 시작해 보십시오.
링크된 .lib 파일은 .dll에 관련되어 있습니다.
저도 같은 문제가 있었어요.프로젝트 My Project와 Test Project가 있다고 가정합니다.My Project용 lib 파일을 Test Project에 효과적으로 링크했습니다.그러나 이 lib 파일은 My Project용 DLL이 구축될 때 생성되었습니다.또한 My Project의 모든 메서드에 대한 소스 코드를 포함하지 않고 DLL의 진입점에 대한 액세스만 포함했습니다.
이 문제를 해결하기 위해 My Project를 LIB로 빌드하고 Test Project를 이 .lib 파일에 링크했습니다(생성된 .lib 파일을 Test Project 폴더에 복사하여 붙여넣습니다).그런 다음 My Project를 DLL로 다시 빌드할 수 있습니다.Test Project가 링크되어 있는 lib에는 My Project 클래스의 모든 메서드에 대한 코드가 포함되어 있기 때문에 컴파일 중입니다.
링커 에러에 대해서는, 이 질문에의 대응이 있는 것 같기 때문에, 여기에 추가하겠습니다.
GCC 5.2.0에서 링커 오류가 발생하는 원인 중 하나는 새로운 libstdc++ 라이브러리 ABI가 기본적으로 선택되기 때문입니다.
std의 유형을 포함하는 심볼에 대한 정의되지 않은 참조에 대한 링커 오류가 발생하는 경우::_cxx11 네임스페이스 또는 태그 [abi:cxx11]는 _GLIBCX_USE_CX11_ABI 매크로에 대해 다른 값으로 컴파일된 오브젝트파일을 링크하려고 하고 있음을 나타냅니다.이 문제는 일반적으로 오래된 버전의 GCC를 사용하여 컴파일된 서드파티 라이브러리에 링크할 때 발생합니다.서드파티 라이브러리를 새로운 ABI로 재구축할 수 없는 경우는, 낡은 ABI로 코드를 재컴파일 할 필요가 있습니다.
따라서 5.1.0 이후 GCC로 전환했을 때 갑자기 링커에러가 발생했을 경우, 이것은 체크할 필요가 있습니다.
링크가 라이브러리를 참조하는 오브젝트 파일보다 먼저 라이브러리를 소비합니다.
- 프로그램을 컴파일하여 GCC 툴체인과 링크하려고 합니다.
- 링크에 필요한 라이브러리와 라이브러리 검색 경로가 모두 지정됩니다.
- if
libfoo
의존하다libbar
에 '아까운데'가 딱 요.libfoo
전에libbar
. - 와의 연계가 실패하다
undefined reference to
에러가 있습니다. - 그러나 정의되지 않은 모든 것이 당신이 가지고 있는 헤더 파일에 선언됩니다.
#include
및 되어 있습니다.d 는 링크하고 있는 라이브러리에서 정의되어 있습니다.
예시는 C에 있습니다.마찬가지로 C++가 될 수 있습니다.
사용자가 직접 구축한 정적 라이브러리와 관련된 최소한의 예
my_lib.c
#include "my_lib.h"
#include <stdio.h>
void hw(void)
{
puts("Hello World");
}
my_lib.h
#ifndef MY_LIB_H
#define MT_LIB_H
extern void hw(void);
#endif
예 1.c
#include <my_lib.h>
int main()
{
hw();
return 0;
}
스태틱 라이브러리를 구축합니다.
$ gcc -c -o my_lib.o my_lib.c
$ ar rcs libmy_lib.a my_lib.o
프로그램을 컴파일합니다.
$ gcc -I. -c -o eg1.o eg1.c
연결시켜 .libmy_lib.a
★★★★
$ gcc -o eg1 -L. -lmy_lib eg1.o
eg1.o: In function `main':
eg1.c:(.text+0x5): undefined reference to `hw'
collect2: error: ld returned 1 exit status
컴파일 및 링크를 한 번에 실행해도 다음과 같은 결과가 나타납니다.
$ gcc -o eg1 -I. -L. -lmy_lib eg1.c
/tmp/ccQk1tvs.o: In function `main':
eg1.c:(.text+0x5): undefined reference to `hw'
collect2: error: ld returned 1 exit status
라이브러리와 예, , 압축 라이브러리libz
예2.c
#include <zlib.h>
#include <stdio.h>
int main()
{
printf("%s\n",zlibVersion());
return 0;
}
프로그램 컴파일:
$ gcc -c -o eg2.o eg2.c
을 로로 your your your와 .libz
★★★★
$ gcc -o eg2 -lz eg2.o
eg2.o: In function `main':
eg2.c:(.text+0x5): undefined reference to `zlibVersion'
collect2: error: ld returned 1 exit status
컴파일 및 링크를 한 번에 실행하는 경우에도 마찬가지입니다.
$ gcc -o eg2 -I. -lz eg2.c
/tmp/ccxCiGn7.o: In function `main':
eg2.c:(.text+0x5): undefined reference to `zlibVersion'
collect2: error: ld returned 1 exit status
은 2번이다, 2번이다.pkg-config
:
$ gcc -o eg2 $(pkg-config --libs zlib) eg2.o
eg2.o: In function `main':
eg2.c:(.text+0x5): undefined reference to `zlibVersion'
뭘 잘못하는 거야?
프로그램을 만들기 위해 링크하려는 개체 파일 및 라이브러리의 시퀀스에서 라이브러리를 참조하는 개체 파일 앞에 배치합니다.라이브러리를 참조하는 오브젝트 파일 뒤에 배치해야 합니다.
예 1을 올바르게 링크합니다.
$ gcc -o eg1 eg1.o -L. -lmy_lib
성공:
$ ./eg1
Hello World
예 2를 올바르게 링크합니다.
$ gcc -o eg2 eg2.o -lz
성공:
$ ./eg2
1.2.8
와 2를 하세요.pkg-config
★★★★★★★★★★★★★★★★★★:
$ gcc -o eg2 eg2.o $(pkg-config --libs zlib)
$ ./eg2
1.2.8
설명
이제부터는 독서가 자유입니다.
디폴트로는 GCC에 의해 생성된 링크명령어는 링크 내의 파일을 왼쪽에서 오른쪽으로 명령줄 순서로 소비합니다.파일이 무엇인가를 참조하고 있고, 그 정의가 포함되어 있지 않은 것을 발견했을 경우, 는 오른쪽 끝에 있는 파일에서 정의를 검색합니다.최종적으로 정의를 찾으면 참조가 해결됩니다.마지막에 해결되지 않은 참조가 있는 경우 링크는 실패합니다.링커는 거꾸로 검색되지 않습니다.
첫 번째 예 1, 정적 라이브러리 사용my_lib.a
정적 라이브러리는 개체 파일의 인덱스된 아카이브입니다.가 " " "를 했을 때-lmy_lib
이 스태틱라이브러리를 을 알 수 있습니다../libmy_lib.a
에 필요한 여부를 libmy_lib.a
.
파일에는 .libmy_lib.a
,,my_lib.o
이 중에서 딱 한 , 1가지밖에 my_lib.o
함수 「」, 「」입니다.hw
.
한 것이 있다고 합니다.my_lib.o
이 「」를 하고 있는 을 이미 있는 에만.hw
중 개에서 되어 있지 hw
.
합니다.my_lib.o
프로그램에 추가합니다.에는 의 되어 있습니다.hw
에 대한 참조입니다.hw
해결되었습니다.
다음과 같이 프로그램을 링크하려고 하면:
$ gcc -o eg1 -L. -lmy_lib eg1.o
링커가 추가되지 않았습니다. eg1.o
프로그램을 볼 수긍정확하게-lmy_lib
그 볼 수 eg1.o
프로그램이 아직 참조하지 않았습니다.hw
: 모든 참조가 에 있기 때문에 아직 참조가 전혀 작성되지 않았습니다.eg1.o
.
는 '링커'를 .my_lib.o
에는 더 .libmy_lib.a
.
다음, '찾다', '찾다', '찾다', '찾다', '찾다',eg1.o
프로그램용으로 추가합니다.링크 시퀀스의 오브젝트 파일은 항상 프로그램에 추가됩니다.이 프로그램은 다음 사항을 참조합니다.hw
의 되어 있지 않습니다.hw
; 그러나 누락된 정의를 제공할 수 있는 링크 시퀀스는 남아 있지 않습니다.에 참조hw
는 해결되지 않고 링크에 실패합니다.
두 번째 예 2, 공유 라이브러리 사용libz
공유 라이브러리는 객체 파일이나 그와 유사한 파일의 아카이브가 아닙니다.이 프로그램에는 더 가깝습니다.main
다른 프로그램이 런타임에 사용할 수 있도록 정의된 다른 여러 기호를 표시합니다.
많은 Linux Linux Distros」GCC)를 .gcc
,g++
,gfortran
etc에 지시합니다.ld
필요에 따라 공유 라이브러리를 링크합니다.그런 디스트로가 하나 있군요.
, 가 「」, 「」를 하면,-lz
시퀀스에서 를 들어 공유 라이브러리 공유 라이브러리(예를 들어 공유 라이브러리)를하고 있는 것을 합니다./usr/lib/x86_64-linux-gnu/libz.so
있지 가 있다, 라는 libz
이것이 참일 경우 링커는 다음 링크에서 청크를 복사하지 않습니다.libz
그 대신, 프로그램의 과 같이 .대신 프로그램 코드를 조작하기만 하면 다음과 같이 됩니다.
시 는 " " "의합니다.
libz
프로그램 복사본을 로드하여 실행할 때마다 프로그램과 동일한 프로세스로 이동합니다.시 이 에 되어 있는 마다.
libz
「export」, 「export」의 되는 합니다.libz
은은과과과과과과
것은 한 입니다.libz
함수 「」, 「」입니다.zlibVersion
됩니다( 「 」( 「 」eg2.c
되어 「」에 의해서 된 libz
참조가 해결되었습니다.
그러나 다음과 같이 프로그램을 링크하려고 하면:
gcc -o eg2 -lz eg2.o
이벤트 순서는 예 1과 같은 방식으로 잘못되어 있습니다.에 의해 검출된 -lz
프로그램 내에는 아무것도 참조되어 있지 않습니다.모두 에 있습니다.eg2.o
을 사용하다는 이 을 사용할 수 합니다.libz
★★★★★★★★★★★★★★★★★★★★★★.eg2.o
이 , 「」가 않습니다.zlibVersion
종료됩니다.되지 않고 에 실패합니다 」이 참조는 해결되지 않고 링크는 실패합니다.
마지막으로 '는요.pkg-config
예 2의 변형은 이제 분명한 설명을 가지고 있다. 확장후: " " " " " :
gcc -o eg2 $(pkg-config --libs zlib) eg2.o
다음과 같이 됩니다.
gcc -o eg2 -lz eg2.o
다시 한 번 예시를 보여드리겠습니다.
예 1에서는 문제를 재현할 수 있지만 예 2에서는 재현할 수 없습니다.
링크:
gcc -o eg2 -lz eg2.o
당신에게는 딱 맞는군요!
(또는: 이 링크는 Fedora 23에서는 정상적으로 동작하지만 Ubuntu 16.04에서는 실패합니다.)
그 이유는 링크가 동작하는 디스트로는 필요에 따라 공유 라이브러리를 링크하도록 GCC 툴체인을 설정하지 않은 디스트로의 하나이기 때문입니다.
옛날에는 Unix와 같은 시스템에서 정적 라이브러리와 공유 라이브러리를 서로 다른 규칙으로 연결하는 것이 일반적이었습니다.링크 시퀀스의 정적 라이브러리는 예 1에서 설명된 필요에 따라 링크되었지만 공유 라이브러리는 무조건 링크되었습니다.
이 동작은 링크 시 경제적입니다.링커는 프로그램에 공유 라이브러리가 필요한지 여부를 고려할 필요가 없기 때문입니다.공유 라이브러리라면 링크하십시오.대부분의 링크 라이브러리는 공유 라이브러리입니다.단점도 있습니다.-
실행 시 공유 라이브러리가 필요하지 않더라도 프로그램과 함께 로드될 수 있기 때문에 비경제적입니다.
라이브러리와 프로그래머에게 스러울 수 프로그래머는, 「스태틱 라이브러리」와「공유 라이브러리」의 링크 룰이 다른지 아닌지를 경우가 .
-lfoo
에서는, 「이러한 링크」가 됩니다./some/where/libfoo.a
「」로/some/where/libfoo.so
공유 라이브러리와 정적 라이브러리의 차이를 이해하지 못할 수도 있습니다.
이 균형은 오늘날 분열의 상황으로 이어졌다.일부 디스트로는 공유 라이브러리의 GCC 링크 규칙을 변경하여 필요에 따라 모든 라이브러리에 적용됩니다.몇몇 디스트리뷰터들은 옛날 방식을 고수하고 있다.
컴파일과 링크를 동시에 해도 이 문제가 발생하는 이유는 무엇입니까?
이렇게 하면:
$ gcc -o eg1 -I. -L. -lmy_lib eg1.c
는 반드시 컴파일해야 .eg1.c
오브젝트 합니다.libmy_lib.a
링크할 때 오브젝트 파일이 필요하다는 것을 어떻게 모를 수 있을까요?
1개의 명령어로 컴파일 및 링크를 실시해도 링크시퀀스의 순서는 변경되지 않습니다.
하면, 「 」가 됩니다.gcc
컴파일 + 링크 필요 여부를 확인합니다.따라서 백그라운드에서 컴파일명령어를 생성하여 실행한 후 링크명령어를 생성하여 실행합니다.다음 2개의 명령어를 실행한 것처럼 합니다.
$ gcc -I. -c -o eg1.o eg1.c
$ gcc -o eg1 -L. -lmy_lib eg1.o
따라서 이들 2개의 명령어를 실행한 경우와 마찬가지로 링크도 실패합니다.이 장애에서 알 수 있는 유일한 차이점은 gcc가 컴파일 + 링크 케이스에서 임시 객체 파일을 생성했다는 것입니다.사용자는 이 파일을 사용하도록 명령하지 않기 때문입니다.eg1.o
· ★★★★★★★★★★★★★★★★.
/tmp/ccQk1tvs.o: In function `main'
다음 대신:
eg1.o: In function `main':
「 」를 참조해 주세요.
상호 종속적인 연결된 라이브러리가 지정된 순서가 잘못되었습니다.
상호의존 라이브러리를 잘못된 순서로 배치하는 것은 정의를 제공하는 파일보다 링크에서 나중에 오는 것에 대한 정의를 필요로 하는 파일을 가져올 수 있는 한 가지 방법입니다.라이브러리를 참조하는 오브젝트 파일 앞에 배치하는 것도 같은 실수를 저지르는 방법입니다.
링커 스크립트를 지원하지 않는 GNU ld의 래퍼
일부 .so 파일은 실제로 GNU ld 링커 스크립트입니다(libtbb 등).따라서 file은 다음 내용을 포함하는 ASCII 텍스트파일입니다
INPUT (libtbb.so.2)
일부 더 복잡한 빌드에서는 이 기능이 지원되지 않을 수 있습니다.예를 들어 컴파일러 옵션에 -v를 포함하면 mainwin gcc wrapper mwdip이 링크할 라이브러리의 상세 출력 목록에서 linker script 명령 파일을 폐기하는 것을 볼 수 있습니다.간단한 회피책은 링커스크립트 입력 명령어파일을 대신 파일(또는 심볼링크)의 복사본으로 바꾸는 것입니다.
cp libtbb.so.2 libtbb.so
를 .so의 풀할 수 를 들어, 「 -l」의 「.so」의 대신에, 」의 「.so」의 풀 패스로 치환할 수 있습니다.★★★★★★★★★★★★★★★★★,-ltbb
하다/home/foo/tbb-4.3/linux/lib/intel64/gcc4.4/libtbb.so.2
템플릿 친구 중...
친구 연산자(또는 함수)와 함께 템플릿 유형의 코드 조각이 지정됩니다.
template <typename T>
class Foo {
friend std::ostream& operator<< (std::ostream& os, const Foo<T>& a);
};
operator<<
는 비표준 함수로 선언되고 있습니다. 타입에 T
함께 Foo
, 비고유, 비고유, 비고유, 비음성, 등이 있습니다.operator<<
타입이 있는 Foo<int>
선언된 경우 다음과 같은 운영자 구현이 있어야 합니다.
std::ostream& operator<< (std::ostream& os, const Foo<int>& a) {/*...*/}
실장되어 있지 않기 때문에 링커는 검출에 실패하고 에러가 발생합니다.
를 " " " 앞에 할 수 .Foo
적절한 인스턴스화를 입력하고 친구로 선언합니다.구문은 조금 어색하지만 다음과 같습니다.
// forward declare the Foo
template <typename>
class Foo;
// forward declare the operator <<
template <typename T>
std::ostream& operator<<(std::ostream&, const Foo<T>&);
template <typename T>
class Foo {
friend std::ostream& operator<< <>(std::ostream& os, const Foo<T>& a);
// note the required <> ^^^^
// ...
};
template <typename T>
std::ostream& operator<<(std::ostream&, const Foo<T>&)
{
// ... implement the operator
}
하는 ''로합니다.Foo
(예:)operator<< <int>
할 수 있도록 제한되어 있습니다.Foo<int>
.
대안은 다음과 같습니다.
다음과 같이 우정이 템플릿의 모든 인스턴스화로 확장되도록 허용합니다.
template <typename T> class Foo { template <typename T1> friend std::ostream& operator<<(std::ostream& os, const Foo<T1>& a); // ... };
「」의
operator<<
클래스 정의 내에서 인라인으로 실행할 수 있습니다.template <typename T> class Foo { friend std::ostream& operator<<(std::ostream& os, const Foo& a) { /*...*/ } // ... };
연산자(또는 함수) 선언이 클래스에만 표시되는 경우 cppreference에서 인수에 의존하는 룩업에만 해당 이름을 사용할 수 없습니다.
클래스 또는 클래스 템플릿 내의 친구 선언에서 처음 선언된 이름은 X의 가장 안쪽을 둘러싼 네임스페이스의 멤버가 되지만 네임스페이스 범위에서 일치하는 선언이 제공되지 않는 한 검색에 액세스할 수 없습니다.
템플릿 친구에 대한 자세한 내용은 cppreference 및 C++ FAQ를 참조하십시오.
상기의 기술을 나타내는 코드 리스트.
장애가 있는 코드샘플에 대한 주의로서 g++는 다음과 같이 경고합니다.
warning: friend declaration 'std::ostream& operator<<(...)' declares a non-template function [-Wnon-template-friend]
note: (if this is not what you intended, make sure the function template has already been declared and add <> after the function name here)
포함 경로가 다른 경우
헤더 파일과 관련된 공유 라이브러리(.lib 파일)가 동기화되지 않으면 링커 오류가 발생할 수 있습니다.제가 설명해 드릴게요.
링커는 어떻게 작동합니까?링커는 시그니처를 비교하여 함수 선언(헤더로 선언)과 정의(공유 라이브러리)를 대조합니다.링커가 완전히 일치하는 함수 정의를 찾지 못하면 링커 오류가 발생할 수 있습니다.
선언과 정의가 일치하는 것 같은데 링커 오류가 발생할 수 있습니까?네! 소스 코드에서는 똑같아 보이지만 컴파일러가 어떻게 보느냐에 따라 다릅니다.기본적으로 다음과 같은 상황이 발생할 수 있습니다.
// header1.h
typedef int Number;
void foo(Number);
// header2.h
typedef float Number;
void foo(Number); // this only looks the same lexically
두 함수 선언이 소스 코드에서는 동일해 보이지만 컴파일러에 따르면 실제로 다른 것에 주목하십시오.
어떻게 그런 상황에 처하게 되었는지 물어보실 수 있습니다.물론 경로도 포함!공유 라이브러리를 컴파일할 때 include 경로를 통해header1.h
해서 '어느 정도'를 쓰게 되고header2.h
자신의 프로그램에서는, 헤더를 긁어 부스럼을 만들어 버립니다(예상 의도한 대로).
실제 세계에서 이러한 일이 어떻게 일어날 수 있는지에 대한 예는 다음과 같습니다.
예를 들어 한층 더 상세하게 설명하다
두 프로젝트가 .graphics.lib
★★★★★★★★★★★★★★★★★」main.exe
★★★★★★★★★★★★★★★★★★★★★★★★★★★★common_math.h
라이브러리가 다음 함수를 내보낸다고 가정합니다.
// graphics.lib
#include "common_math.h"
void draw(vec3 p) { ... } // vec3 comes from common_math.h
그런 다음 자신의 프로젝트에 라이브러리를 포함시킵니다.
// main.exe
#include "other/common_math.h"
#include "graphics.h"
int main() {
draw(...);
}
쾅! 링커 오류가 발생하는데 왜 실패하는지 알 수 없습니다.는 일반 에서는 같은 입니다.common_math.h
(이 예에서는, 다른 패스를 포함시켜 명확하게 하고 있습니다만, 항상 명확하게는 할 수 없습니다.을 포함하다
에서 찾을 수 .draw()
실제로는 라이브러리에서 내보내고 있는 것이 분명합니다.뭐가 잘못됐는지 머리를 긁적거리면서 몇 시간을 보낼 수 있을 거야.중요한 것은 파라미터 타입이 약간 다르기 때문에 링커는 다른 시그니처를 인식합니다.예에서는 " " 입니다.vec3
컴파일러에 관한 한 두 프로젝트 모두 다른 유형입니다.이 문제는 2개의 인크루드 파일이 약간 다르기 때문에 발생할 수 있습니다(인크루드 파일은 2개의 다른 버전의 라이브러리에서 온 것일 수 있습니다).
링커의 디버깅
DUMPBIN은 Visual Studio를 사용하는 경우 친구입니다.다른 컴파일러들도 비슷한 도구를 가지고 있을 거예요.
프로세스는 다음과 같습니다.
- 링커 에러에 기재되어 있는 이상한 이름(draw@graphics@X 등)에 주의해 주세요.YZ)
- 라이브러리에서 내보낸 기호를 텍스트 파일로 덤프합니다.
- 내보낸 관심 기호를 검색하면 이름이 서로 다릅니다.
- 뒤죽박죽이 된 이름들이 왜 다르게 되었는지 주목하라.소스 코드에서는 동일한 것처럼 보이지만 파라미터 유형이 다르다는 것을 알 수 있습니다.
- 그들이 다른 이유.위의 예에서는 인크루드 파일이 다르기 때문에 서로 다릅니다.
[1] 프로젝트란 라이브러리 또는 실행 파일을 생성하기 위해 서로 링크된 소스 파일 세트를 의미합니다.
EDIT 1: 첫 번째 섹션을 알기 쉽게 다시 작성.다른 수정이 필요하시면 아래의 코멘트를 부탁드립니다.감사합니다!
이 없다UNICODE
(Definitions)
는 Windows UNICODE 를 사용하여 됩니다.TCHAR
'''로 됩니다.wchar_t
타타 with with with with with with with with with with with with with with with with with 가 아닌 경우UNICODE
with with with with with with with with with with with with with with with with with로 되어 TCHAR
char
이런 거.이것들UNICODE
★★★★★★★★★★★★★★★★★」_UNICODE
defin은 모든 ""T
문자열 유형에 영향을 줍니다.LPTSTR
,LPCTSTR
그리고 고라니.
구축:UNICODE
.UNICODE
에서는 "defined" 의 정의가 합니다.의 정의에 불일치가 있기 때문에 링커 오류가 발생합니다.TCHAR
char
★★wchar_t
.
오류에는 보통 이 함수는 값이 "예"로 있습니다.char
★★★★★★★★★★★★★★★★★」wchar_t
파생형, 이것들은 다음을 포함할 수 있습니다.std::basic_string<>
코드할 때 참조할 수 있습니다.TCHAR
★★★★★★★★★★★★★★★★★」std::basic_string<TCHAR>
UNICODE의 멀티바이트 문자('Multi-Byte Character'('Multi-Byte 문자')입니다.
한 모든 를 일관성 해야 합니다.UNICODE
(그리고)_UNICODE
를 참조해 주세요.
이것은, 어느쪽인가를 사용해 실시할 수 있습니다.
#define UNICODE #define _UNICODE
또는 프로젝트 설정에서
[ Project Properties ]> [ General ]> [ Project Defaults ]> [ Character Set ]
또는 명령줄에 있습니다.
/DUNICODE /D_UNICODE
UNICODE 를 사용하지 않는 경우는, 정의가 설정되어 있지 않은 것을 확인해 주세요.또한 프로젝트에서 다중 문자 설정이 사용되고 일관되게 적용됩니다.
"Release" 빌드 및 "Debug" 빌드 간에도 일관성을 유지하는 것을 잊지 마십시오.
청소 및 재구축
빌드를 "깨끗하게" 하면 이전 빌드, 실패한 빌드, 불완전한 빌드 및 기타 빌드 시스템 관련 빌드 문제에서 남아 있는 "죽은 목재"를 제거할 수 있습니다.
일반적으로 IDE 또는 빌드에 "클린" 기능이 포함되어 있지만, 이 기능이 올바르게 설정되어 있지 않거나(수동 makefile 등), 실패하거나(중간 바이너리 또는 결과 바이너리가 읽기 전용으로 되어 있는 경우가 있습니다.
"클린"이 완료되면 "클린"이 성공하고 생성된 모든 중간 파일(자동 makefile 등)이 성공적으로 제거되었는지 확인합니다.
이 프로세스는 최종 수단으로 간주할 수 있지만, 특히 오류와 관련된 코드가 최근에 추가된 경우(로컬 또는 소스 저장소 중 하나에서) 좋은 첫 번째 단계가 될 수 있습니다.
'누락되어 .const
선언C++)
에서 온 에게는 C C++가 C++로 있는 .const
변수에는 내부(또는 정적) 링크가 있습니다.에서는 모든 으로 C로 되어 있기 .extern
(가, 음, 음, 음, 음)의 경우)static
키워드가 없습니다).
예:
// file1.cpp
const int test = 5; // in C++ same as "static const int test = 5"
int test2 = 5;
// file2.cpp
extern const int test;
extern int test2;
void foo()
{
int x = test; // linker error in C++ , no error in C
int y = test2; // no problem
}
헤더 파일을 사용하여 file2.cpp 및 file1.cpp에 포함시키는 것이 옳습니다.
extern const int test;
extern int test2;
'하다'를 도 있다.const
와 명시적인 file1.cpp의 변수extern
여러 개의 답변이 받아들여진 꽤 오래된 질문이지만, 불분명한 "정의되지 않은 참조" 오류를 해결하는 방법을 공유하고자 합니다.
다른 버전의 라이브러리
하여 에일리어스를 .std::filesystem::path
: 파일 시스템은 C++17 이후 표준 라이브러리에 있지만, 내 프로그램도 C++14로 컴파일해야 하기 때문에 변수 에일리어스를 사용하기로 했습니다.
#if (defined _GLIBCXX_EXPERIMENTAL_FILESYSTEM) //is the included filesystem library experimental? (C++14 and newer: <experimental/filesystem>)
using path_t = std::experimental::filesystem::path;
#elif (defined _GLIBCXX_FILESYSTEM) //not experimental (C++17 and newer: <filesystem>)
using path_t = std::filesystem::path;
#endif
main.cpp, file.h, file.cpp의 3가지 파일이 있다고 칩니다.
- file.h #experimental:: filesystem > 이며 위의 코드가 포함되어 있습니다.
- file.cpp, file.h, #filename의 "file" 구현입니다.h"
- main.cpp #filesystem과 "file"을 지정합니다.h"
main.cpp 및 file.h에서 사용되는 라이브러리는 다릅니다.main.cpp #include'd "file" 이후.<filesystem> 뒤에 있는h" 파일 시스템의 버전은 C++17 이었습니다.나는 다음과 같은 명령으로 프로그램을 컴파일하곤 했다.
g++ -g -std=c++17 -c main.cpp
main를 main> main.합니다.오오오오오오오오오오오오오오오오오오오오오g++ -g -std=c++17 -c file.cpp
및 file> file.cpp file.h file.o로 컴파일합니다.
$g++ -g -std=c++17 -o executable main.o file.o -lstdc++fs
-> main.o와 file.o 링크
이와 같이 file.o에 포함되어 main.o에서 사용되는 함수는 main.o가 file.o를 참조했기 때문에 "정의되지 않은 참조" 오류를 발생시켰습니다.
결의안
이 문제를 해결하려면 file.h의 <experimental::filesystem>을 <filesystem>으로 변경하면 됩니다.
공유 라이브러리에 대해 링크할 때는 사용된 기호를 숨기지 않도록 하십시오.
gcc의 기본 동작은 모든 기호가 표시된다는 것입니다.단, 번역 유닛을 옵션으로 빌드할 경우-fvisibility=hidden
, 로 표시된 기능/기능만__attribute__ ((visibility ("default")))
는 결과 공유 오브젝트의 외부입니다.
다음 명령을 실행하여 찾고 있는 기호가 외부인지 여부를 확인할 수 있습니다.
# -D shows (global) dynamic symbols that can be used from the outside of XXX.so
nm -D XXX.so | grep MY_SYMBOL
숨김/로컬 기호는 다음과 같이 표시됩니다.nm
예를 들어 소문자 기호 유형이 있는 경우t
코드 섹션의 경우 T 대신:
nm XXX.so
00000000000005a7 t HIDDEN_SYMBOL
00000000000005f8 T VISIBLE_SYMBOL
를 사용할 수도 있습니다.nm
옵션으로-C
(C++가 사용된 경우) 이름을 분리한다.
Windows-dls와 마찬가지로 퍼블릭 함수를 정의로 표시할 수 있습니다.DLL_PUBLIC
다음과 같이 정의:
#define DLL_PUBLIC __attribute__ ((visibility ("default")))
DLL_PUBLIC int my_public_function(){
...
}
이는 대략 Windows/MSVC 버전과 일치합니다.
#ifdef BUILDING_DLL
#define DLL_PUBLIC __declspec(dllexport)
#else
#define DLL_PUBLIC __declspec(dllimport)
#endif
가시성에 대한 자세한 내용은 gcc wiki를 참조하십시오.
변환 유닛이 컴파일된 경우-fvisibility=hidden
결과 기호에는 여전히 외부 링크가 있습니다(대문자 기호 유형:nm
오브젝트 파일이 정적 라이브러리의 일부가 되어도 문제없이 외부 링크에 사용할 수 있습니다.오브젝트 파일이 공유 라이브러리에 링크되어 있는 경우에만 링크가 로컬이 됩니다.
개체 파일에서 숨겨진 기호를 찾으려면 다음을 수행합니다.
>>> objdump -t XXXX.o | grep hidden
0000000000000000 g F .text 000000000000000b .hidden HIDDEN_SYMBOL1
000000000000000b g F .text 000000000000000b .hidden HIDDEN_SYMBOL2
함수 또는 클래스 메서드는 소스 파일에 정의되어 있습니다.inline
지정자.
예:-
main.cpp
#include "gum.h"
#include "foo.h"
int main()
{
gum();
foo f;
f.bar();
return 0;
}
foo.h (1)
#pragma once
struct foo {
void bar() const;
};
gum.h (1)
#pragma once
extern void gum();
foo.cpp (1)
#include "foo.h"
#include <iostream>
inline /* <- wrong! */ void foo::bar() const {
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
gum.cpp (1)
#include "gum.h"
#include <iostream>
inline /* <- wrong! */ void gum()
{
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
그것을 지정하면gum
(표준,foo::bar
)는inline
그 정의에서 컴파일러는 인라인화 됩니다.gum
(선택한 경우), 기준:
- 고유한 정의를 전혀 발산하지 않는 모습
gum
, 따라서 - 링커가 정의를 참조할 수 있는 기호를 방출하지 않는다
gum
, 그 대신 - 모든 콜의 치환
gum
컴파일된 본문의 인라인 복사를 사용하여gum
.
그 결과,gum
소스 파일의 인라인gum.cpp
, 오브젝트 파일로 컴파일 됩니다.gum.o
에의 모든 콜이gum
링커가 참조할 수 있는 기호는 정의되어 있지 않습니다.gum
. 링크할 때gum.o
다른 오브젝트 파일과 함께 프로그램으로 변환합니다)main.o
외부 기호를 참조할 수 있습니다.gum
링커는 이러한 참조를 해결할 수 없습니다.따라서 링크에 장애가 발생합니다.
컴파일:
g++ -c main.cpp foo.cpp gum.cpp
링크:
$ g++ -o prog main.o foo.o gum.o
main.o: In function `main':
main.cpp:(.text+0x18): undefined reference to `gum()'
main.cpp:(.text+0x24): undefined reference to `foo::bar() const'
collect2: error: ld returned 1 exit status
정의할 수 있는 것은gum
~하듯이inline
컴파일러가 그 정의를 모든 소스 파일에서 볼 수 있다면gum
호출할 수 있습니다.즉, 그 인라인 정의는 컴파일하는 모든 소스 파일에 포함된 헤더 파일에 존재해야 합니다.gum
호출할 수 있습니다.다음 두 가지 작업 중 하나를 수행합니다.
정의를 인라인화하지 않음
를 삭제합니다.inline
소스 파일 정의의 지정자:
foo.cpp (2)
#include "foo.h"
#include <iostream>
void foo::bar() const {
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
gum.cpp (2)
#include "gum.h"
#include <iostream>
void gum()
{
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
이것으로 재구축:
$ g++ -c main.cpp foo.cpp gum.cpp
imk@imk-Inspiron-7559:~/develop/so/scrap1$ g++ -o prog main.o foo.o gum.o
imk@imk-Inspiron-7559:~/develop/so/scrap1$ ./prog
void gum()
void foo::bar() const
성공.
또는 올바르게 인라인
헤더 파일의 인라인 정의:
foo.h (2)
#pragma once
#include <iostream>
struct foo {
void bar() const { // In-class definition is implicitly inline
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
};
// Alternatively...
#if 0
struct foo {
void bar() const;
};
inline void foo::bar() const {
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
#endif
gum.h (2)
#pragma once
#include <iostream>
inline void gum() {
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
이제 우리는 필요 없다foo.cpp
또는gum.cpp
:
$ g++ -c main.cpp
$ g++ -o prog main.o
$ ./prog
void gum()
void foo::bar() const
언급URL : https://stackoverflow.com/questions/73597285/cant-compile-due-to-a-undefined-references
'programing' 카테고리의 다른 글
is_syslogx) vs $x === PHP에서는 null입니다. (0) | 2022.12.09 |
---|---|
Larabel MySql 연결 문제가 너무 많습니다. (0) | 2022.12.09 |
MySQL 데이터베이스에서 가능한 열거 값을 가져오려면 어떻게 해야 합니까? (0) | 2022.12.09 |
MySQL 문에 PHP 변수를 포함하는 방법 (0) | 2022.12.09 |
알기 쉽게 설명하자면 Java에서 Runnable이란 무엇입니까? (0) | 2022.12.09 |