一直对编译这个事情不是太明白,想好好学习一下,今天自己写东西试验,遇到了这么个问题,想请教一下,先贴代码
// test.h
#ifndef JUST_FOR_TEST_H_
#define JUST_FOR_TEST_H_
struct Test {
static int i;
void t();
};
int Test::i = 0;
#endif
// test.cpp
#include "test.h"
void Test::t() { }
// main.cpp
#include "test.h"
int main() {
Test t;
t.t();
}
本来觉得这个是很简单的,但是链接的时候报了错,提示是
ld: 1 duplicate symbol for architecture x86_64
将test.h中静态成员的定义放到cpp文件中就好了,但是我很不理解,不是使用了头文件保护吗?这样不是已经确保了一个头文件在一个工程中只会被包含一次吗?是我哪里的理解出了偏差了吗?希望各位高手可以指点一下,本人菜鸡,不是学计算机出身的,所以问的问题可能很傻,如果可以的话希望可以讲的详细一点,如果可以介绍两本学习的书就更好了,先谢过了!
짧은 답변:
headguard는 헤더 파일이 두 번 포함되는 것을 방지할 수만 있습니다. 컴파일할 때 문제는 i가 다른 컴파일 단위로 정의된다는 것입니다.
대부분의 IDE를 컴파일할 때 모든 소스 파일은 별도로 타겟 파일로 컴파일되고 최종적으로 타겟 파일은 커넥터로 연결됩니다. 대상 파일 간의 연결 관계는 해당 소스 파일의 포함을 통해 추론됩니다. 헤더 파일은 컴파일에 참여하지 않습니다. 여기서 IDE가 생성한 컴파일 스크립트는 거의
clang main.cpp test.cpp
입니다. 물론, 그 외에도 프로젝트 설정에는 많은 컴파일 옵션이 있습니다.귀하의 예에서 컴파일러는 test.cpp 및 main.cpp를 컴파일하여 대상 파일을 생성합니다. 그런 다음 커넥터는 두 개의 대상 파일을 결합합니다. 정적 멤버 변수 i는 헤더 파일에 정의되어 있으므로 두 소스 파일 모두 이 헤더 파일을 포함합니다. 결과적으로 i는 두 개의 컴파일 단위에서 한 번 정의됩니다. 최종 연결 시 연결 오류가 발생합니다.
긴 답변:
귀하께서 발생한 링크 오류는 실제로 ODR 위반으로 인해 발생한 것입니다.
ODR 계약:
하지만 이 외에도 특별한 경우가 많습니다. 나는 그것들을 여기에 나열하지 않을 것입니다(주로 그것들을 모두 나열할 수 없기 때문입니다...).
번역 단위, 정의, 프로그램 세 가지 개념이 매우 중요합니다.
번역 단위는 컴파일 명령에 따라 결정되는 여러 컴파일 단위가 있습니다. 컴파일 단위의 컴파일 결과를 객체 파일이라고 합니다. definition은 정의입니다. 이 예에서
int Test::i = 0;
는 정적 멤버 변수i
의 정의입니다. ODR에 따르면 이 정의는 전체 프로그램에서 한 번만 나타날 수 있습니다. 프로그램은 여러 대상 파일로 연결되는 프로그램입니다.컴파일 지침:
clang main.cpp test.cpp -o main
이 컴파일에는 두 개의 컴파일 단위 main.cpp와 test.cpp가 있습니다. 이들은 별도로 대상 파일로 컴파일됩니다.
으아악#include
은 해당 파일의 내용을 소개하는 사전 컴파일된 매크로이며, 사전 컴파일된 모든 명령은 소스 코드가 컴파일되기 전에 실행됩니다. 즉, 컴파일 단위 "test.cpp"(또는 clang 컴파일러가 수신한 코드)의 내용은 다음과 같습니다.컴파일 단위 "main.cpp"의 내용은 다음과 같습니다.
으아악모든 매크로가 실행되고 주석은 컴파일러에서 무시됩니다.
이 두 컴파일 단위는 각각 test.o 및 main.o와 같은 대상 파일로 컴파일됩니다. 컴파일 작업이 완료되었습니다. 그런 다음 컴파일러는 두 개의 개체 파일 test.o와 main.o를 하나의 프로그램으로 연결합니다. 이 연결 중에 ODR 위반이 발생합니다.
test.cpp에
int Test::i = 0;
이 정의되어 있는 경우. 그러면 이 정의는 main.cpp에 나타나지 않으며 ODR이 위반되지 않습니다.