정의되지 않은 참조 / 해결되지 않은 외부 심볼 오류는 무엇이며 어떻게 해결합니까?


1493

정의되지 않은 참조 / 해결되지 않은 외부 심볼 오류는 무엇입니까? 일반적인 원인은 무엇이며 어떻게 해결 / 방지합니까?

자유롭게 편집하거나 추가하십시오.


@LuchianGrigore '답변 을 추가하십시오 ' 허용하고 싶은 경우 기본 링크 인 IMHO (관련 링크)를 추가하는 것이 좋습니다.
πάντα ῥεῖ

19
이 질문은 유용한 답변을 인정하기에는 너무 일반적입니다. 나는 질문을 쏘지 않고 1000 명 이상의 평판을 가진 사람들이 이것에 대해 논평하는 것을 믿을 수 없다. 그 동안 나는 닫혀있는 현명하고 매우 유용한 질문을 많이 본다.
Albert van der Horst

10
@AlbertvanderHorst "이 질문은 유용한 답변을 인정하기에는 너무 일반적입니다"아래 답변이 유용하지 않습니까?
LF

4
너무 일반적으로 표시되는 질문에 대한 많은 답변과 마찬가지로 유용 할 수 있습니다.
Albert van der Horst

1
나는 대부분의 새로운 사용자에게 정직하게 요구하는 최소한의 재현 가능한 예를보고 싶습니다. 나는 아무것도 의미하지 않습니다. 단지 – 우리는 사람들이 우리 자신에게 강요하지 않는 규칙을 따를 것을 기대할 수 없습니다.
Danilo

답변:


848

2.2 에서 지정한대로 C ++ 프로그램 컴파일은 여러 단계로 수행됩니다 (참조 용 Keith Thompson의 신용) .

번역의 구문 규칙 중 우선 순위는 다음 단계에 따라 지정됩니다 [각주 참조] .

  1. 실제 소스 파일 문자는 구현 정의 방식으로 필요한 경우 기본 소스 문자 세트 (행 끝 표시기의 개행 문자 소개)에 맵핑됩니다. [한조각]
  2. 줄 바꿈 문자 바로 뒤에 오는 백 슬래시 문자 (\)의 각 인스턴스는 삭제되어 실제 소스 행을 연결하여 논리 소스 행을 형성합니다. [한조각]
  3. 소스 파일은 사전 처리 토큰 (2.5)과 일련의 공백 문자 (주석 포함)로 분해됩니다. [한조각]
  4. 전처리 지시문이 실행되고 매크로 호출이 확장되며 _Pragma 단항 연산자식이 실행됩니다. [한조각]
  5. 문자 리터럴 또는 문자열 리터럴의 각 소스 문자 세트 멤버와 문자 리터럴 또는 비원시 문자열 리터럴의 각 이스케이프 시퀀스 및 범용 문자 이름은 실행 문자 세트의 해당 멤버로 변환됩니다. [한조각]
  6. 인접 문자열 리터럴 토큰이 연결되었습니다.
  7. 토큰을 분리하는 공백 문자는 더 이상 중요하지 않습니다. 각 전처리 토큰은 토큰으로 변환됩니다. (2.7). 결과 토큰은 구문 및 의미 론적으로 분석되고 번역 단위로 번역됩니다. [한조각]
  8. 번역 된 번역 단위와 인스턴스화 단위는 다음과 같이 결합됩니다. [SNIP]
  9. 모든 외부 엔티티 참조가 해결되었습니다. 라이브러리 구성 요소는 현재 번역에 정의되지 않은 엔터티에 대한 외부 참조를 충족시키기 위해 연결됩니다. 이러한 모든 변환기 출력은 실행 환경에서 실행하는 데 필요한 정보가 포함 된 프로그램 이미지로 수집됩니다. (강조 광산)

[각주] 실제로는 다른 단계가 함께 접힐 수 있지만 구현은 이러한 개별 단계가 발생하는 것처럼 작동해야합니다.

지정된 오류는이 마지막 컴파일 단계에서 발생하며 가장 일반적으로 연결이라고합니다. 기본적으로 많은 구현 파일을 객체 파일 또는 라이브러리로 컴파일했으며 이제 함께 작동하도록 만들고 싶습니다.

당신이 기호를 정의 말 aa.cpp. 이제 그 기호를 b.cpp 선언 하고 사용했습니다. 연결하기 전에 단순히 해당 심볼이 어딘가에 정의되었다고 가정 하지만 아직 어디에도 신경 쓰지 않습니다. 연결 단계는 심볼을 찾아서 심볼을 올바르게 링크하는 역할을합니다 b.cpp(실제로 심볼 을 사용하는 객체 또는 라이브러리에 연결).

Microsoft Visual Studio를 사용하는 경우 프로젝트가 .lib파일을 생성하는 것을 볼 수 있습니다. 여기에는 내 보낸 심볼 테이블과 가져온 심볼 테이블이 포함됩니다. 가져온 심볼은 링크 한 라이브러리에 대해 분석되고 해당 심볼을 사용하는 라이브러리 .lib(있는 경우)에 대해 내 보낸 심볼이 제공됩니다 .

다른 컴파일러 / 플랫폼에도 비슷한 메커니즘이 존재합니다.

일반적인 오류 메시지는 error LNK2001, error LNK1120, error LNK2019에 대한 마이크로 소프트 비주얼 스튜디오undefined reference to symbolname 만GCC .

코드:

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

일반적인 원인은 다음과 같습니다.


16
개인적으로 MS 링커 오류 메시지는 GCC 오류처럼 읽을 수 있다고 생각합니다. 또한 해결되지 않은 외부에 대해 엉망이고 이름이없는 이름을 모두 포함 할 수 있다는 장점이 있습니다. 맹 글링 된 이름을 갖는 것은 문제가 무엇인지 확인하기 위해 라이브러리 또는 객체 파일을 직접보아야 할 때 도움이 될 수 있습니다 (예 : 호출 규칙 불일치). 또한 어떤 버전의 MSVC에서 오류가 발생했는지 잘 모르겠지만 최신 버전에는 해결되지 않은 외부 기호를 참조하는 함수의 이름 (혼합 및 엉킴 방지)이 포함되어 있습니다.
Michael Burr

5
David Drysdale 은 링커 작동 방식에 대한 훌륭한 기사를 작성했습니다 : 링커 초보자 안내서 . 이 질문의 주제를 감안할 때 유용하다고 생각했습니다.
Pressacco

@TankorSmash gcc를 사용 하시겠습니까? MinGW가 더 정확합니다.
doug65536

1
@luchian 위의 오류를 수정하여 올바른 것을 추가하면 좋을 것입니다
Phalgun

177

반원 :

순수한 virtual소멸자는 구현이 필요합니다.

소멸자를 순수로 선언하려면 일반 함수와 달리 여전히 정의해야합니다.

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

초기화는 static const클래스 정의 내에서 정수 또는 열거 유형 의 데이터 멤버에 제공 될 수 있습니다 . 그러나이 멤버의 odr-use를 사용하려면 위에서 설명한 네임 스페이스 범위 정의가 여전히 필요합니다. C ++ 11은 모든 static const데이터 멤버에 대해 클래스 내에서 초기화 할 수 있습니다 .


두 가지를 모두 수행하는 것이 가능하며 dtor는 실제로 예외가 아니라고 강조하고 싶을뿐입니다. (언뜻보기에는 당신의 표현에서 분명하지 않습니다.)
Deduplicator

112

적절한 라이브러리 / 오브젝트 파일에 대한 링크 실패 또는 구현 파일 컴파일

일반적으로 각 변환 단위는 해당 변환 단위에 정의 된 기호의 정의가 포함 된 객체 파일을 생성합니다. 해당 심볼을 사용하려면 해당 객체 파일과 연결해야합니다.

아래 GCC 명령 줄에서 서로 연결하는 모든 오브젝트 파일을 지정하거나 함께 구현 파일을 컴파일합니다.

g++ -o test objectFile1.o objectFile2.o -lLibraryName

다음 libraryName은 플랫폼 별 추가없이 라이브러리의 이름입니다. 따라서 예를 들어 Linux 라이브러리 파일은 일반적으로 호출 libfoo.so되지만 쓰기 만합니다 -lfoo. Windows에서 동일한 파일은이라고 할 수 foo.lib있지만 동일한 인수를 사용합니다. 를 사용하여 해당 파일을 찾을 수있는 디렉토리를 추가해야 할 수도 있습니다 -L‹directory›. -l또는 뒤에 공백을 쓰지 마십시오 -L.

들어 엑스 코드 : -> 라이브러리 검색 경로 추가 - 사용자 헤더 검색 경로 추가> 드래그를 프로젝트 폴더에 실제 라이브러리 참조를 놓습니다.

아래 MSVS , 프로젝트에 추가 된 파일은 자동으로 오브젝트 파일을 서로 연결하고이 lib파일 (일반적인 사용에서) 생성 될 것이다. 별도의 프로젝트에서 기호를 사용하려면 lib프로젝트 설정에 파일 을 포함해야합니다 . 이는 프로젝트 속성의 링커 섹션에서 수행됩니다 Input -> Additional Dependencies. ( lib파일 경로 는에 추가되어야합니다. Linker -> General -> Additional Library Directories) lib파일 과 함께 제공되는 타사 라이브러리를 사용할 때 일반적으로 그렇게하지 않으면 오류가 발생합니다.

컴파일에 파일을 추가하는 것을 잊어 버릴 수도 있습니다.이 경우 개체 파일이 생성되지 않습니다. gcc 에서는 파일을 명령 줄에 추가합니다. 에서 MSVS이 프로젝트에 파일을 추가가 (파일 수이기는하지만, 수동으로, 개별적으로 빌드에서 제외) 자동으로 컴파일 할 것이다.

Windows 프로그래밍에서 필요한 라이브러리를 연결하지 않았다는 의미없는 기호는 해석되지 않은 기호의 이름이로 시작한다는 것입니다 __imp_. 설명서에서 함수 이름을 찾아보고 사용해야하는 라이브러리가 있어야합니다. 예를 들어 MSDN은 "라이브러리"라는 섹션의 각 함수 맨 아래에있는 상자에 정보를 넣습니다.


1
gcc main.c대신에 gcc main.c other.c (일반적으로 프로젝트가 .o 파일을 빌드 할 정도로 커지기 전에 종종하는) 실수를 명시 적으로 다룰 수 있다면 좋을 것 입니다.
MM

101

선언되었지만 변수 나 함수를 정의하지 않았습니다.

전형적인 변수 선언은

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)

불일치의 다른 예는 다음과 같습니다.

  • 한 네임 스페이스에서 선언되고 다른 네임 스페이스에서 정의 된 함수 / 변수
  • 클래스 멤버로 선언 된 함수 / 변수로 전역으로 정의되거나 그 반대로 정의됩니다.
  • 함수 반환 형식, 매개 변수 번호 및 형식 및 호출 규칙이 모두 일치하지는 않습니다.

컴파일러의 오류 메시지는 종종 선언되었지만 정의되지 않은 변수 또는 함수의 전체 선언을 제공합니다. 제공 한 정의와 밀접하게 비교하십시오. 모든 세부 사항이 일치하는지 확인하십시오.


VS 에서 소스 디렉토리에 추가#includes 되지 않은 헤더의 파일과 일치하는 cpp 파일 도 누락 된 정의 범주에 속합니다.
Laurie Stearn

86

상호 종속 링크 라이브러리가 지정된 순서가 잘못되었습니다.

라이브러리가 서로 의존하는 경우 라이브러리가 연결된 순서는 중요합니다. 도서관이 경우 일반적으로, 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

다시 반복 그래서, 순서 합니까 문제!


제 경우에는 공유 라이브러리에 의존하는 하나의 객체 파일이 있다는 것이 궁금합니다. 나는 Makefile을 수정했다 라이브러리를 넣어 후에 데비안에 GCC 4.8.4와 객체. gcc 4.4의 Centos 6.5에서는 Makefile이 아무런 문제없이 작동했습니다.
Marco Sulla

74

"정의되지 않은 참조 / 해결되지 않은 외부 심볼"

"정의되지 않은 참조 / 해결되지 않은 외부 심볼"이 무엇인지 설명하려고합니다.

참고 : 나는 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

좋아, readelf 출력 참조 (기호)

$ readelf --symbols src1.o
8: 0000000000000000     4 OBJECT  GLOBAL DEFAULT    3 local_var_name

이제 local_var_name이 GLOBAL을 바인드했습니다 (기존 LOCAL).

링크

$ g++ src1.o src2.o -o prog

그리고 그것을 실행

$ ./prog 
123456789

좋아, 우리는 그것을 해킹 :)

따라서 링커가 객체 파일에서 전역 심볼을 찾을 수없는 경우 "정의되지 않은 참조 / 해결되지 않은 외부 심볼 오류"가 발생합니다.


71

기호는 C 프로그램에서 정의되었으며 C ++ 코드에서 사용되었습니다.

함수 (또는 변수) void foo()는 C 프로그램에서 정의되었으며 C ++ 프로그램에서 사용하려고합니다.

void foo();
int main()
{
    foo();
}

C ++ 링커는 이름이 엉망이 될 것으로 예상하므로 함수를 다음과 같이 선언해야합니다.

extern "C" void foo();
int main()
{
    foo();
}

마찬가지로 C 프로그램에서 정의되는 대신 함수 (또는 변수) void foo()는 C ++에서 정의되었지만 C 연결로 정의되었습니다.

extern "C" void foo();

C ++ 연결을 사용하여 C ++ 프로그램에서 사용하려고합니다.

전체 라이브러리가 헤더 파일에 포함되어 있고 C 코드로 컴파일 된 경우 포함은 다음과 같아야합니다.

extern "C" {
    #include "cheader.h"
}

5
또는 당신이 C 라이브러리를 개발하는 경우 반대로, 멋진 규칙은 모든 수출 선언을 주변에서 헤더 파일을 보호하는 것입니다 #ifdef __cplusplus [\n] extern"C" { [\n] #endif#ifdef __cplusplus [\n] } [\n] #endif( [\n]실제 캐리지 리턴되는하지만 난 의견에 이것을 제대로 쓸 수 없습니다).
Bentoy13

위의 의견에서와 같이 여기에 '혼합 언어 헤더 만들기'섹션이 도움이되었습니다. oracle.com/technetwork/articles/servers-storage-dev/…
zanbri

정말 나를 도와 주었다! 내가이 질문을 찾을 때 이것은 내 경우였다
knightofhorizon

extern C : 로 둘러싸인 우연히 일반 C ++ 헤더 파일을 포함하는 경우에도 발생할 수 있습니다 extern "C" { #include <myCppHeader.h> }.
ComFreek

68

다른 모든 방법이 실패하면 다시 컴파일하십시오.

최근에 문제를 일으키는 파일을 다시 컴파일하여 Visual Studio 2012에서 해결되지 않은 외부 오류를 제거 할 수있었습니다. 내가 재건하면 오류가 사라졌습니다.

이는 일반적으로 두 개 이상의 라이브러리에 주기적 종속성이있는 경우 발생합니다. 라이브러리 A는 B.lib의 기호를 사용하려고 시도하고 라이브러리 B는 A.lib의 기호를 사용하려고 시도합니다. 둘 다 처음부터 존재하지 않습니다. A 컴파일을 시도하면 B.lib를 찾을 수 없으므로 링크 단계가 실패합니다. A.lib가 생성되지만 dll은 생성되지 않습니다. 그런 다음 B를 컴파일하면 B.lib가 생성됩니다. 이제 B.lib가 발견되었으므로 A를 다시 컴파일하면 작동합니다.


1
올바른-라이브러리에 주기적 종속성이있는 경우 발생합니다.
Luchian Grigore

1
나는 당신의 대답을 넓히고 주요 답변에 연결했습니다. 감사.
Luchian Grigore

57

모듈 / dll (컴파일러 특정)에서 메소드 / 클래스 가져 오기 / 내보내기

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 참조 ).

비슷한 가져 오기 / 내보내기 클래스를 사용할 수 있습니다.

class DLLIMPEXP X
{
};

2
완료하려면이 답변에 GCC visibility및 Windows .def파일 이 언급되어야합니다.이 파일은 심볼 이름과 존재에 영향을 미치기 때문입니다.
rubenvb

@rubenvb 나는 .def나이에 파일을 사용하지 않았습니다 . 답변을 추가하거나 수정하십시오.
Luchian Grigore

57

이것은 모든 VC ++ 프로그래머가 반복해서 본 가장 혼란스러운 오류 메시지 중 하나입니다. 먼저 명확성을 만들어 봅시다.

A. 상징이란 무엇입니까? 간단히 말해서 기호는 이름입니다. 변수 이름, 함수 이름, 클래스 이름, typedef 이름 또는 C ++ 언어에 속하는 이름 및 부호를 제외한 모든 이름이 될 수 있습니다. 의존성 라이브러리 (다른 사용자 정의)에 의해 사용자 정의되거나 도입됩니다.

B. 외부는 무엇입니까? VC ++에서 모든 소스 파일 (.cpp, .c 등)은 변환 단위로 간주되며, 컴파일러는 한 번에 하나의 단위를 컴파일하고 현재 변환 단위에 대해 하나의 오브젝트 파일 (.obj)을 생성합니다. (이 소스 파일에 포함 된 모든 헤더 파일은 사전 처리되어이 번역 단위의 일부로 간주 됨) 번역 단위 내의 모든 항목은 내부로 간주되며 다른 모든 항목은 외부로 간주됩니다. C ++에서, 당신은 같은 키워드를 사용하여 외부 기호를 참조 할 수있다 extern, __declspec (dllimport)등등을.

C.“해결”이란 무엇입니까? 해결은 연결 시간 용어입니다. 연결시 링커는 내부적으로 정의를 찾을 수없는 객체 파일의 모든 심볼에 대한 외부 정의를 찾으려고합니다. 이 검색 프로세스의 범위는 다음을 포함합니다.

  • 컴파일 시간에 생성 된 모든 객체 파일
  • 이 건물 응용 프로그램의 추가 종속성으로 명시 적 또는 암시 적으로 지정된 모든 라이브러리 (.lib).

이 검색 프로세스를 resolve라고합니다.

D. 마지막으로 왜 미해결 외부 기호인가? 링커가 내부적으로 정의가없는 심볼에 대한 외부 정의를 찾을 수없는 경우, 해결되지 않은 외부 심볼 오류를보고합니다.

E. LNK2019의 가능한 원인 : 해결되지 않은 외부 기호 오류. 링커가 외부 심볼의 정의를 찾지 못했기 때문에이 오류가 발생한다는 것을 이미 알고 있습니다. 가능한 원인은 다음과 같이 정렬 할 수 있습니다.

  1. 정의가 존재합니다

예를 들어, a.cpp에 foo라는 함수가 정의되어 있다면 :

int foo()
{
    return 0;
}

b.cpp에서는 foo 함수를 호출하고 싶습니다.

void foo();

함수 foo ()를 선언하고 다른 함수 본문에서 호출하려면 다음과 같이하십시오 bar().

void bar()
{
    foo();
}

이제이 코드를 빌드하면 LNK2019 오류가 발생하여 foo가 해결되지 않은 기호임을 불평합니다. 이 경우, 우리는 foo ()가 a.cpp에서 정의를 가지고 있지만 우리가 호출하는 것과는 다르다는 것을 알고 있습니다 (다른 반환 값). 정의가 존재하는 경우입니다.

  1. 정의가 존재하지 않습니다

라이브러리에서 일부 함수를 호출하려고하지만 가져 오기 라이브러리가 Project | Properties | Configuration Properties | Linker | Input | Additional Dependency프로젝트 설정 의 추가 종속성 목록 (:에서 설정 )에 추가되지 않은 경우 . 이제 정의는 현재 검색 범위에 없으므로 링커에서 LNK2019를보고합니다.


1
체계적인 설명에 감사드립니다. 이 답변을 읽은 후에 다른 답변을 이해할 수 있습니다.
displayName

55

템플릿 구현이 보이지 않습니다.

특수화되지 않은 템플릿은 해당 정의를 사용하는 모든 번역 단위에 정의가 표시되어야합니다. 즉, 템플릿 정의를 구현 파일로 분리 할 수 ​​없습니다. 구현을 분리해야하는 경우 일반적인 해결 방법은 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헤더 파일 또는이를 사용하는 번역 단위에 보이는 곳으로 이동해야 합니다.

전문화 된 템플릿은 구현 파일에서 구현할 수 있으며 구현을 볼 필요는 없지만 전문화는 이전에 선언해야합니다.

자세한 설명과 다른 가능한 솔루션 (명시 적 인스턴스화) 은이 질문과 답변을 참조하십시오 .


1
"템플릿은 헤더에 정의되어야합니다"에 대한 자세한 내용은 stackoverflow.com/questions/495021
PlasmaHH

41

정의되지 않은 참조 WinMain@16또는 유사한 '비정상적인' main() 진입 점 참조 (특히).

실제 IDE에 적합한 프로젝트 유형을 선택하지 못한 경우가 있습니다. IDE는 일반적으로 사용되는 int main(int argc, char** argv);서명 대신 Windows Application 프로젝트를 이러한 진입 점 함수 (위의 누락 된 참조에 지정된대로)에 바인딩 할 수 있습니다 .

IDE가 일반 콘솔 프로젝트 를 지원 하는 경우 Windows 응용 프로그램 프로젝트 대신이 프로젝트 유형을 선택할 수 있습니다.


다음은 케이스 가구 1case2 A로부터 더 자세히 다루어 실제 문제는.


2
도움이 될 수는 없지만 이 질문 과 이것이 종종 주요 기능이 없기 때문에 발생한다는 사실을 지적하십시오 WinMain. 유효한 C ++ 프로그램에는가 필요합니다 main.
chris

36

또한 타사 라이브러리를 사용하는 경우 올바른 32/64 비트 바이너리가 있는지 확인하십시오


34

Microsoft는 #pragma링크 타임에 올바른 라이브러리를 참조 할 수 있는 기능 을 제공합니다 .

#pragma comment(lib, "libname.lib")

라이브러리의 디렉토리를 포함하여 라이브러리 경로 외에도 라이브러리의 전체 이름이어야합니다.


34

새 도구 세트 버전을 사용하려면 Visual Studio NuGet 패키지를 업데이트해야합니다.

방금 libpng와 Visual Studio 2013을 연결하려고 할 때이 문제가 발생했습니다. 문제는 패키지 파일에 Visual Studio 2010 및 2012 용 라이브러리 만 있다는 것입니다.

올바른 해결책은 개발자가 업데이트 된 패키지를 릴리스 한 다음 업그레이드하기를 희망하지만 VS2012 라이브러리 파일을 가리키는 VS2013의 추가 설정을 해킹하여 나에게 도움이되었습니다.

모든 섹션을 복사하여 해당 파일 packages을 찾고 내부 에서 패키지를 솔루션 디렉토리 내부 의 폴더 에서 편집했습니다 . 나는 변화 에 의 조건 필드에만 모두 같은 파일 이름 경로를 떠날 매우 조심 . 이를 통해 Visual Studio 2013은 2012 년의 라이브러리에 연결될 수 있었으며이 경우에는 효과가있었습니다.packagename\build\native\packagename.targetsv110v110v120v110


1
이것은 지나치게 구체적 인 것 같습니다. 아마도 새로운 스레드 가이 답변에 더 적합한 곳 일 것입니다.
Luchian Grigore

3
@LuchianGrigore : 이 질문은 정확히이 문제 였으므로 여기 에 게시하고 싶었지만 이 질문의 중복으로 표시되어 답변을 드릴 수 없었습니다. 그래서 대신 대답을 여기에 게시했습니다.
Malvineous

그 질문에는 이미 받아 들여진 대답이 있습니다. 일반적인 원인이 위에 나열되어 있으므로 중복으로 표시되어 있습니다. 포함되지 않은 라이브러리의 모든 문제에 대해 여기에 답변이 있으면 어떻게됩니까?
Luchian Grigore

6
@LuchianGrigore :이 문제는 라이브러리에만 국한된 것이 아니며 Visual Studio의 패키지 관리 시스템을 사용하는 모든 라이브러리에 영향을 미칩니다. 우리 둘 다 libpng에 문제가 있었기 때문에 다른 질문을 찾았습니다. 또한 libxml2, libiconv 및 glew에 대해 동일한 솔루션으로 동일한 문제가 발생했습니다. 이 질문은 Visual Studio 패키지 관리 시스템의 문제에 관한 것이며, 제 대답은 그 이유를 설명하고 해결 방법을 제공합니다. 누군가 방금 "해결되지 않은 외부"를보고 실제로 패키지 관리 문제인 경우 표준 링커 문제라고 가정했습니다.
Malvineous

34

수천 개의 .cpp 파일과 수천 개의 .h 파일이있는 c ++로 작성된 큰 프로젝트가 있다고 가정하고이 프로젝트는 10 개의 정적 라이브러리에 의존한다고 가정 해 봅시다. 우리가 Windows에 있고 Visual Studio 20xx에서 프로젝트를 빌드한다고 가정 해 봅시다. Ctrl + F7 Visual Studio를 눌러 전체 솔루션 컴파일을 시작하면 (솔루션에 프로젝트가 하나만 있다고 가정하십시오)

컴파일의 의미는 무엇입니까?

  • Visual Studio에서 파일 .vcxproj를 검색 하고 확장자가 .cpp 인 각 파일을 컴파일하기 시작합니다. 컴파일 순서는 정의되어 있지 않으므로 main.cpp 파일이 먼저 컴파일되었다고 가정해서는 안됩니다.
  • .cpp 파일이 파일에 정의되거나 정의되지 않은 기호를 찾기 위해 추가 .h 파일에 의존하는 경우
  • 컴파일러에서 하나의 심볼을 찾을 수없는 .cpp 파일이 하나 있으면 컴파일러 시간 오류로 인해 심볼 x를 찾을 수 없습니다.
  • 확장명이 .cpp 인 각 파일에 대해 개체 파일 .o가 생성되고 Visual Studio는 링커에서 처리해야하는 모든 개체 파일이 포함 된 ProjectName.Cpp.Clean.txt 라는 파일에 출력을 씁니다 .

두 번째 컴파일 단계는 링커가 수행합니다. 링커는 모든 객체 파일을 병합하고 최종적으로 출력 (실행 파일 또는 라이브러리 일 수 있음)을 빌드해야합니다.

프로젝트 연결 단계

  • 모든 객체 파일을 구문 분석하고 헤더에서만 선언 된 정의를 찾으십시오 (예 : 이전 답변에서 언급 한 클래스의 한 메소드 코드 또는 클래스 내부의 멤버 인 정적 변수 초기화 이벤트)
  • 하나 개의 심볼은 오브젝트 파일에서 찾을 수 없습니다 경우 그는 또한 프로젝트에 새 라이브러리를 추가하는 추가 Libraries.For에서 검색되는 구성 속성 -> VC ++ 디렉터리는 -> 라이브러리 디렉토리 여기에 당신은 도서관과 검색을위한 추가 폴더를 지정한 구성 속성 > - 링커 -> 라이브러리 이름을 지정하기위한 입력 . -링커가 .cpp에 쓴 기호를 찾지 못하면 링커 시간 오류가 발생 하여 다음과 같이 들릴 수 있습니다. error LNK2001: unresolved external symbol "void __cdecl foo(void)" (?foo@@YAXXZ)

관측

  1. 링커가 한 심볼을 찾으면 다른 라이브러리에서 해당 심볼을 검색하지 않습니다.
  2. 라이브러리 연결 순서 는 중요 합니다.
  3. 링커가 하나의 정적 라이브러리에서 외부 심볼을 ​​찾으면 프로젝트 출력에 심볼을 포함하지만 라이브러리가 공유되는 경우 (dynamic) 출력에 코드 (symbols)를 포함하지 않지만 런타임 충돌이 발생할 수 있습니다 나오다

이런 종류의 오류를 해결하는 방법

컴파일러 시간 오류 :

  • C ++ 프로젝트의 구문이 올바른지 확인하십시오.

링커 시간 오류

  • 헤더 파일에 선언 한 모든 심볼을 정의하십시오.
  • #pragma once컴파일러가 현재 .cpp에 이미 포함되어있는 경우 하나의 헤더를 포함하지 않도록 컴파일러에 사용
  • 외부 라이브러리에 헤더 파일에 정의한 다른 기호와 충돌 할 수있는 기호가 포함되어 있지 않은지 확인하십시오
  • 템플리트를 사용하여 컴파일러가 인스턴스화에 적합한 코드를 생성 할 수 있도록 헤더 파일에 각 템플리트 함수의 정의를 포함시켜야합니다.

귀하의 답변은 Visual Studio에만 해당되지 않습니까? 이 질문은 IDE / 컴파일러 도구를 지정하지 않으므로 비 스튜디오 스튜디오 부분에는 대답을 쓸모 없게 만듭니다.
Victor Polevoy

네가 옳아 . 그러나 컴파일 / 링크의 모든 IDE 프로세스마다 약간 다르게 수행됩니다. 그러나 파일은 정확히 동일하게 처리됩니다 (g ++에서도 플래그를 구문 분석 할 때도 동일한 작업을 수행합니다.)

문제는 실제로 IDE에 관한 것이 아니라 문제를 연결하기위한 답변에 관한 것입니다. 연결 문제는 IDE와 관련이 없지만 컴파일러 및 빌드 프로세스와 관련이 있습니다.
Victor Polevoy

예. 그러나 빌드 / 링크 프로세스는 g ++ / Visual Studio (Microsoft에서 VS 용 컴파일러 제공) / Eclipse / Net Beans에서 같은 방식으로 수행됩니다.

29

컴파일러 / IDE의 버그

최근 에이 문제가 발생 했으며 Visual Studio Express 2013의 버그였습니다 . 버그를 극복하기 위해 프로젝트에서 소스 파일을 제거하고 다시 추가해야했습니다.

컴파일러 / IDE의 버그 일 수 있다고 생각되면 시도하는 단계 :

  • 프로젝트 정리 (일부 IDE에는이를 수행 할 수있는 옵션이 있으며 오브젝트 파일을 삭제하여 수동으로 수행 할 수도 있음)
  • 원본 프로젝트의 모든 소스 코드를 복사하여 새 프로젝트를 시작하십시오.

5
도구가 파손되었다고 믿는 것이 실제 원인에서 멀어 질 것입니다. 컴파일러가 문제를 일으킨 것보다 실수했을 가능성이 훨씬 높습니다. 솔루션을 정리하거나 빌드 구성을 다시 작성하면 빌드 오류가 해결 될 수 있지만 컴파일러에 버그가있는 것은 아닙니다. 링크 된 "버그 인 것으로 나타났습니다"는 Microsoft에 의해 확인되지 않으며 재현 할 수 없습니다.
JDiMatteo

4
@JDiMatteo이 질문에 대한 답변은 21 개이므로 상당량의 답변이 "우연한"해결책이되지는 않습니다. 가능성 임계 값 아래에있는 모든 답변을 닫으면 대부분의 일반적인 경우가 쉽게 발견되므로이 페이지가 효과적으로 쓸모 없게됩니다.
developerbmw

27

링커를 사용하여 오류 진단

대부분의 최신 링커에는 다양한 각도로 인쇄하는 자세한 옵션이 있습니다.

  • 링크 호출 (명령 줄)
  • 링크 단계에 어떤 라이브러리가 포함되어 있는지에 대한 데이터
  • 도서관의 위치
  • 사용 된 검색 경로.

gcc와 clang의 경우; 일반적으로 -v -Wl,--verbose또는 -v -Wl,-v명령 줄에 추가 합니다. 자세한 내용은 여기를 참조하십시오.

MSVC의 경우, /VERBOSE특히 /VERBOSE:LIB링크 명령 줄에 추가됩니다.


26

연결된 .lib 파일은 .dll과 연결되어 있습니다.

나는 같은 문제가 있었다. MyProject 및 TestProject 프로젝트가 있다고 가정 해보십시오. MyProject의 lib 파일을 TestProject에 효과적으로 연결했습니다. 그러나이 lib 파일은 MyProject의 DLL이 빌드 될 때 생성되었습니다. 또한 MyProject의 모든 메소드에 대한 소스 코드를 포함하지 않았지만 DLL의 진입 점에만 액세스합니다.

이 문제를 해결하기 위해 MyProject를 LIB로 빌드하고 TestProject를이 .lib 파일에 링크했습니다 (생성 된 .lib 파일을 TestProject 폴더에 복사하여 붙여 넣기). 그런 다음 MyProject를 DLL로 다시 빌드 할 수 있습니다. TestProject가 연결된 lib에 MyProject 클래스의 모든 메소드에 대한 코드가 포함되어 있으므로 컴파일 중입니다.


25

링커 오류와 관련하여 사람들 이이 질문에 대한 것으로 보이므로 여기에 이것을 추가하려고합니다.

GCC 5.2.0에서 링커 오류가 발생하는 한 가지 이유는 이제 새로운 libstdc ++ 라이브러리 ABI가 기본적으로 선택되어 있기 때문입니다.

std :: __ cxx11 네임 스페이스 또는 태그 [abi : cxx11]의 유형을 포함하는 기호에 대한 정의되지 않은 참조에 대한 링커 오류가 발생하면 _GLIBCXX_USE_CXX11_ABI에 대해 서로 다른 값으로 컴파일 된 오브젝트 파일을 함께 연결하려고 함을 나타냅니다. 매크로. 이전 버전의 GCC로 컴파일 된 타사 라이브러리에 연결할 때 일반적으로 발생합니다. 타사 라이브러리를 새 ABI로 다시 빌드 할 수없는 경우 이전 ABI로 코드를 다시 컴파일해야합니다.

따라서 5.1.0 이후에 GCC로 전환 할 때 갑자기 링커 오류가 발생하면이를 확인해야합니다.


20

링커 스크립트를 지원하지 않는 GNU ld를 감싸는 래퍼

일부 .so 파일은 실제로 GNU ld 링커 스크립트입니다 . 예를 들어 libtbb.so 파일은 다음 내용이 포함 된 ASCII 텍스트 파일입니다.

INPUT (libtbb.so.2)

좀 더 복잡한 빌드는 이것을 지원하지 않을 수 있습니다. 예를 들어, 컴파일러 옵션에 -v를 포함하면 mainwin gcc 래퍼 mwdip 가 링크 할 자세한 라이브러리 출력 목록에서 링커 스크립트 명령 파일을 삭제 한다는 것을 알 수 있습니다 . 간단한 해결 방법은 링커 스크립트 입력 명령을 바꾸는 것입니다. 대신 파일 사본이있는 파일 (또는 심볼릭 링크), 예 :

cp libtbb.so.2 libtbb.so

또는 -l 인수를 .so의 전체 경로로 바꿀 수 있습니다 (예 : -ltbbdo 대신)./home/foo/tbb-4.3/linux/lib/intel64/gcc4.4/libtbb.so.2


20

링크는 라이브러리를 참조하는 오브젝트 파일보다 먼저 라이브러리를 사용합니다.

  • 프로그램을 컴파일하고 GCC 툴체인과 연결하려고합니다.
  • 연계는 필요한 모든 라이브러리 및 라이브러리 검색 경로를 지정합니다.
  • 경우 libfoo에 따라 libbar풋 올바르게 다음 연결, libfoolibbar.
  • 귀하의 연결이 실패 undefined reference to 뭔가 오류가 발생합니다.
  • 그러나 모든 정의되지 않은 뭔가 들 당신이 가지고있는 헤더 파일에 선언 된 #includeD를 당신이 연결되는 라이브러리에 정의 된 사실이다.

예제는 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

eg1.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

eg2.c

#include <zlib.h>
#include <stdio.h>

int main()
{
    printf("%s\n",zlibVersion());
    return 0;
}

프로그램을 컴파일하십시오 :

$ gcc -c -o eg2.o eg2.c

프로그램을 연결 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의 변형은 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에서 생성 한 링크 명령은 배포판에서 링크의 파일을 명령 줄 순서대로 왼쪽에서 오른쪽으로 사용합니다. 파일이 무언가를 참조 하고 이에 대한 정의를 포함하지 않는 것을 발견하면 파일 의 오른쪽에있는 정의를 검색합니다. 결국 정의를 찾으면 참조가 해결됩니다. 끝에 참조가 해결되지 않은 상태로 남아 있으면 연결이 실패합니다. 링커는 뒤로 검색하지 않습니다.

먼저 정적 라이브러리가있는 예제 1my_lib.a

정적 라이브러리는 객체 파일의 색인화 된 아카이브입니다. 링커가 -lmy_lib링크 순서를 찾아서 이것이 정적 라이브러리를 참조한다는 ./libmy_lib.a것을 알아 내면 프로그램에 객체 파일이 필요한지 여부를 알고 싶어합니다 libmy_lib.a.

단지 객체 파일에 있습니다 libmy_lib.amy_lib.o,과에 정의 된 단 한 가지있다 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대신 정의하는 여러 개의 다른 기호를 노출하여 다른 프로그램이 런타임에 사용할 수 있습니다.

대부분의 리눅스 배포판 오늘은 GCC 툴체인 그래서 그 언어 드라이버 (구성 gcc, g++, gfortran시스템 링커 (지시한다 등) ld온 링크 공유 라이브러리) 등의 필요에 기초를. 그 배포판 중 하나가 있습니다.

이것은 링커가 -lz링키지 순서에서 찾을 때 이것이 공유 라이브러리 (예 : 공유 라이브러리)를 /usr/lib/x86_64-linux-gnu/libz.so참조한다는 것을 알면 아직 정의되지 않은 프로그램에 추가 된 참조에 정의가 있는지 여부를 알고 싶어한다는 의미입니다. 에 의해 수출libz

이것이 사실이라면 링커는 청크를 복사하지 않고libz 프로그램에 추가 하지 않습니다 . 대신 프로그램 코드를 닥터하여 다음과 같이 할 것입니다.

  • 런타임에 시스템 프로그램 로더는 프로그램의 사본을 libz로드하여 실행할 때마다 프로그램과 동일한 프로세스에 사본을로드합니다.

  • 런타임시 프로그램이에 정의 된 libz것을 참조 할 때마다 해당 참조는 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

다시 예제 2입니다.

예제 1에서는 문제를 재현 할 수 있지만 예제 2에서는 재현 할 수 없습니다

연계 :

gcc -o eg2 -lz eg2.o

당신을 위해 잘 작동합니다!

(또는 : 페도라 23에서는 링크가 잘 작동했지만 우분투 16.04에서는 실패했습니다)

연결이 작동하는 배포판 이 필요에 따라 공유 라이브러리를 연결하도록 GCC 툴체인을 구성하지 않는 배포판 중 하나이기 때문 입니다.

예전에는 유닉스 계열 시스템이 다른 규칙으로 정적 라이브러리와 공유 라이브러리를 연결하는 것이 일반적이었습니다. 연결 시퀀스의 정적 라이브러리는 예제 1에서 설명한 필요에 따라 연결되었지만 공유 라이브러리는 무조건 연결되었습니다.

링커는 프로그램에서 공유 라이브러리가 필요한지 여부를 고려할 필요가 없으므로 링크 타임에 경제적입니다. 공유 라이브러리 인 경우 링크하십시오. 그리고 대부분의 연계에서 대부분의 라이브러리는 공유 라이브러리입니다. 그러나 단점도 있습니다.

  • 공유 라이브러리가 필요하지 않더라도 공유 라이브러리를 프로그램과 함께로드 할 수 있기 때문에 런타임에 비 경제적 입니다.

  • 정적 및 공유 라이브러리에 대한 다른 연결 규칙이 있는지 여부를 알 수 없습니다 미숙 프로그래머로 혼동 할 수 있습니다 -lfoo자신의 연계가 해결하는 것입니다에 /some/where/libfoo.a하거나 /some/where/libfoo.so, 어쨌든 공유 및 정적 라이브러리의 차이를 이해하지 않을 수 있습니다.

이 절충안은 오늘날 제도적 상황을 초래했습니다. 일부 배포판에서는 필요에 따라 원칙이 모든 라이브러리에 적용 되도록 공유 라이브러리에 대한 GCC 연계 규칙을 변경했습니다 . 일부 배포판은 구식 방식을 고수했습니다.

동시에 컴파일하고 링크해도이 문제가 계속 발생하는 이유는 무엇입니까?

내가 방금 할 경우 :

$ gcc -o eg1 -I. -L. -lmy_lib eg1.c

반드시 gcc는 eg1.c먼저 컴파일 한 다음 결과 객체 파일을로 연결해야합니다 libmy_lib.a. 링크를 할 때 객체 파일이 필요한지 어떻게 알 수 있습니까?

단일 명령을 사용하여 컴파일하고 연결해도 연결 순서의 순서는 변경되지 않기 때문입니다.

위의 명령을 실행하면 gcc컴파일 + 연결이 필요하다는 것을 알 수 있습니다. 그래서 무대 뒤에서, 그것은 컴파일 명령을 생성하고, 그것을 실행 후 연결 명령을 생성하고, 것처럼, 그것을 실행 당신이 두 개의 명령을 실행했다 :

$ gcc -I. -c -o eg1.o eg1.c
$ gcc -o eg1 -L. -lmy_lib eg1.o

그래서 링크는 경우가하는 것처럼하지 않는 두 명령을 실행합니다. 실패에서 눈에 띄는 차이점은 gcc가 컴파일 + 링크 사례에서 임시 객체 파일을 생성했다는 것 eg1.o입니다. 우리는보다:

/tmp/ccQk1tvs.o: In function `main'

대신에:

eg1.o: In function `main':

또한보십시오

상호 의존적 인 링크 된 라이브러리가 지정된 순서가 잘못되었습니다

상호 의존적 인 라이브러리를 잘못된 순서 로 놓는 것은 정의를 제공 하는 파일보다 나중에 링크에 나오는 것들에 대한 정의 가 필요한 파일을 얻을 수있는 한 가지 방법 일뿐 입니다. 라이브러리를 참조하는 오브젝트 파일 앞에 라이브러리를 두는 것도 같은 실수를하는 또 다른 방법입니다.


18

친구 사귀기 ...

친구 연산자 (또는 함수)가있는 템플리트 유형의 코드 스 니펫이 제공됩니다.

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 내의 친구 선언에서 처음 선언 된 이름은 X의 가장 안쪽에있는 네임 스페이스의 멤버가되지만 네임 스페이스 범위에서 일치하는 선언이 없으면 X를 고려하는 인수 종속 조회를 제외하고 액세스 할 수 없습니다. 제공 ...

cppreferenceC ++ 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)


16

포함 경로가 다른 경우

헤더 파일과 관련 공유 라이브러리 (.lib 파일)가 동기화되지 않으면 링커 오류가 발생할 수 있습니다. 설명하겠습니다.

링커는 어떻게 작동합니까? 링커는 서명을 비교하여 함수 선언 (헤더에 선언 됨)과 정의 (공유 라이브러리에 있음)를 일치시킵니다. 링커가 완벽하게 일치하는 함수 정의를 찾지 못하면 링커 오류가 발생할 수 있습니다.

선언과 정의가 일치하는 것처럼 보이지만 링커 오류가 계속 발생할 수 있습니까? 예! 소스 코드에서는 동일하게 보이지만 실제로는 컴파일러가 보는 내용에 따라 다릅니다. 기본적으로 다음과 같은 상황이 발생할 수 있습니다.

// header1.h
typedef int Number;
void foo(Number);

// header2.h
typedef float Number;
void foo(Number); // this only looks the same lexically

소스 코드에서 두 함수 선언이 어떻게 동일하게 보이지만 컴파일러에 따라 실제로는 다릅니다.

그런 상황에서 어떻게 끝날지 물을 수 있습니까? 물론 경로포함하십시오 ! 공유 라이브러리를 컴파일 할 때 포함 경로가 나오고 자신의 프로그램에서 header1.h사용하게 header2.h되면 헤더가 긁 혀서 무슨 일이 일어 났는지 궁금해 할 것입니다.

현실에서 어떻게 이런 일이 일어날 수 있는지에 대한 예가 아래에 설명되어 있습니다.

예제를 통한 추가 설명

두 가지 프로젝트가 있습니다 : graphics.libmain.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컴파일러에 관한 한 두 프로젝트에서 다른 유형입니다. 이것은 파일이 약간 다른 두 개의 포함 파일에서 나왔기 때문에 발생할 수 있습니다 (포함 파일은 서로 다른 두 버전의 라이브러리에서 온 것 같습니다).

링커 디버깅

DUMPBIN은 Visual Studio를 사용하는 경우 친구입니다. 다른 컴파일러에는 다른 유사한 도구가 있다고 확신합니다.

과정은 다음과 같습니다.

  1. 링커 오류에 주어진 이상한 맹 글링 된 이름에 유의하십시오. (예 : draw @ graphics @ XYZ).
  2. 라이브러리에서 내 보낸 심볼을 텍스트 파일로 덤프합니다.
  3. 내 보낸 관심 기호를 검색하고 맹 글링 된 이름이 다른지 확인하십시오.
  4. 맹 글링 된 이름이 다른 이유에주의를 기울이십시오. 매개 변수 유형이 소스 코드에서 동일하게 보이지만 매개 변수 유형이 다르다는 것을 알 수 있습니다.
  5. 그들이 다른 이유. 위의 예에서 포함 파일이 다르기 때문에 서로 다릅니다.

[1] 프로젝트 라 함은 라이브러리 또는 실행 파일을 생성하기 위해 서로 링크 된 소스 파일 세트를 의미합니다.

편집 1 : 이해하기 쉽도록 첫 번째 섹션을 다시 작성했습니다. 다른 사항을 수정해야하는지 알려면 아래에 의견을 남겨주세요. 감사!


15

일치하지 않는 UNICODE정의

윈도우 유니 코드 빌드로 구축 TCHAR등으로 정의되고 wchar_t있는 건물하지 않을 경우 등 UNICODE으로 빌드 정의 TCHAR정의 char이 등 UNICODE_UNICODE정의는 모든 영향 " T"문자열 유형 ; LPTSTR, LPCTSTR그들의 엘크.

UNICODE정의 된 라이브러리 하나를 빌드하고 정의 UNICODE되지 않은 프로젝트에서 라이브러리 를 연결하려고하면 의 정의에 불일치가 있으므로 링커 오류가 발생합니다 TCHAR. charwchar_t.

에러는 일반적으로 값 함수 포함 char또는 wchar_t파생 형은 다음을 포함 할 수 std::basic_string<>도 등. 코드의 영향을받는 기능을 통해 검색 할 때 종종이 참조 될 것이다 TCHAR또는 std::basic_string<TCHAR>등이 코드가 원래 ( "축소"또는) 빌드를 유니 코드와 멀티 바이트 문자 모두를위한되었다는 고자질하는 표지판입니다 .

이 문제를 해결하려면 UNICODE(and _UNICODE) 의 일관된 정의로 필요한 모든 라이브러리와 프로젝트를 빌드하십시오 .

  1. 이것은 어느 쪽이든 할 수 있습니다;

    #define UNICODE
    #define _UNICODE
  2. 또는 프로젝트 설정에서;

    프로젝트 속성> 일반> 프로젝트 기본값> 문자 세트

  3. 또는 명령 행에서;

    /DUNICODE /D_UNICODE

대안은 UNICODE를 사용하지 않을 경우, 정의가 설정되지 않았는지 및 / 또는 다중 문자 설정이 프로젝트에서 사용되고 일관되게 적용되는지 확인하십시오.

"릴리스"와 "디버그"빌드간에 일관성을 유지하는 것을 잊지 마십시오.


14

청소 및 재건

빌드의 "깨끗한"은 이전 빌드, 실패한 빌드, 불완전한 빌드 및 기타 빌드 시스템 관련 빌드 문제에서 남아있을 수있는 "죽은 나무"를 제거 할 수 있습니다.

일반적으로 IDE 또는 빌드에는 어떤 형태의 "clean"기능이 포함되지만, 이는 올바르게 구성되지 않았거나 (예 : 수동 makefile에서) 실패 할 수 있습니다 (예 : 중간 또는 결과 바이너리가 읽기 전용).

"clean"이 완료되면 "clean"이 성공하고 생성 된 모든 중간 파일 (예 : 자동화 된 makefile)이 성공적으로 제거되었는지 확인하십시오.

과정은 최종 수단으로 볼 수 있지만 종종 좋은 첫 단계입니다 . 특히 오류와 관련된 코드가 최근에 추가 된 경우 (로컬 또는 소스 리포지토리에서).


10

const변수 선언 / 정의 에서 "extern"이 누락 됨 (C ++ 만 해당)

C에서 온 사람들 const에게는 C ++에서 전역 변수에 내부 (또는 정적) 연결 이 있다는 것이 놀랍습니다 . 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;

또는 constfile1.cpp 에서 변수를 명시 적으로 선언 할 수 있습니다extern


8

이 답변은 여러 번의 대답으로 꽤 오래된 질문이지만 모호한 "정의되지 않은 참조"오류 를 해결하는 방법을 공유하고 싶습니다 .

다른 버전의 라이브러리

나는 별칭을 사용하여 참조했다 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의 세 파일이 있다고 가정 해 봅시다.

  • file.h #include 의 < Experimental :: filesystem > 위의 코드를 포함합니다
  • file.cpp , file.h 구현, #include 의 " file.h "
  • main.cpp #include 의 < 파일 시스템 > 및 " file.h "

main.cpp 및 file.h에서 사용되는 다른 라이브러리에 유의하십시오. MAIN.CPP 번호가 "include'd 때문에 file.h를 <후" 파일 시스템 > 파일 시스템의 버전이 사용 하였다 는 C ++ 17 하나 . 다음 명령으로 프로그램을 컴파일했습니다.

$ g++ -g -std=c++17 -c main.cpp-> main.cpp를 main.o로 컴파일
$ g++ -g -std=c++17 -c file.cpp-> 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를에 사용되는 필요가path_t 있기 때문에 "정의되지 않은 참조"오류를 준 main.o를이 언급 std::filesystem::path하지만 file.ostd::experimental::filesystem::path.

해결

이 문제를 해결하려면 file.h의 <experimental :: filesystem>을 <filesystem> 으로 변경 해야했습니다 .


5

공유 라이브러리에 연결할 때 사용 된 기호가 숨겨져 있지 않은지 확인하십시오.

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 

숨겨진 / 로컬 기호는 코드 섹션의 경우`T 대신에 nm소문자 기호 유형으로 표시됩니다 t.

nm XXX.so
00000000000005a7 t HIDDEN_SYMBOL
00000000000005f8 T VISIBLE_SYMBOL

nm옵션 -C을 사용 하여 이름을 엉 키게 할 수도 있습니다 (C ++가 사용 된 경우).

Windows-dll과 유사하게 다음과 같이 정의 된 public 함수를 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 위키에서 확인할 수 있습니다.


변환 단위가 -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

nm -CD또는 nm -gCD을 사용 하여 외부 기호를 보아야합니다 . GCC 위키의 공개 설정 도 참조하십시오 .
jww

2

다른 아키텍처

다음과 같은 메시지가 표시 될 수 있습니다.

library machine type 'x64' conflicts with target machine type 'X86'

이 경우 사용 가능한 기호가 컴파일하는 것과 다른 아키텍처에 대한 것임을 의미합니다.

Visual Studio에서 이는 잘못된 "플랫폼"으로 인한 것이며 올바른 버전을 선택하거나 올바른 버전의 라이브러리를 설치해야합니다.

Linux에서는 잘못된 라이브러리 폴더 ( 예 : lib대신 사용)로 인한 것일 수 있습니다 lib64.

MacOS에는 두 아키텍처를 동일한 파일로 제공하는 옵션이 있습니다. 링크는 두 버전이 모두있을 것으로 예상하지만 하나만 있습니다. 라이브러리가 선택되는 잘못된 lib/ lib64폴더에 문제가있을 수도 있습니다 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.