extern과 static은 링커의 linkage process와 관련이 있는 키워드다.
extern은 파일 바깥쪽에서 링크를 할 수 있는 외부 링크(external link) 키워드이고
static는 파일 안쪽에서만 접근할 수 있는 내부 링크(internal link) 키워드다.
extern은 해당 식별자의 정의(심볼)가 translation unit 파일 바깥쪽에 있다고 링커가 인식한다.
즉, extern으로 선언을 할 경우 외부 object 파일의 정의(심볼)를 참조한다는 의미이다.
※ 기본적으로 일반 함수는 extern 키워드를 붙였다고 간주한다.
--------------
abc.cpp
int a = 10; // 전역변수 a
--------------
main.cpp
// int a = 7; // error 7! 이미 정의된 기호 (abc.cpp)
extern int a; // abc.cpp의 a를 링킹함 (※include를 안한 상태!)
cout << a << endl; // 10
--------------
a가 전역 변수일 경우 이미 다른 파일에서 선언된 a를 재선언할 수 없으며(error 7),
지역 변수인 경우는 상관없다. 다음은 extern 함수의 예제이다.
--------------
abc.cpp
void func(){
cout << "hi" << endl;
}
--------------
main.cpp
extern void func(); // abc.cpp의 func()를 링킹함
func(); // "hi"
--------------
static은 반대로 자신 translation unit 파일 안에서만 접근이 가능하게 하는 키워드다.
즉, static으로 선언된 변수나 함수는 외부 파일에서 접근이 불가능하고 수정될 수 없다.
(외부에서 extern으로 들어와도 말이다.)
--------------
abc.cpp
static int a = 10;
static void func() {
///
}
--------------
main.cpp
extern int a; // 외부의 a에 링킹 시도
extern void func();
cout << a << endl; // 확인할 수 없는 외부 참조(기호)
func(); // 확인할 수 없는 외부 참조(기호)
--------------
main.cpp(2)
int a = 0; // static으로 인해 독자적으로 변수 생성 가능
void func() { cout << "no" << endl; }
cout << a << endl; // 0
func(); // "no"
--------------
static으로 선언된 식별자는 외부 파일에서 접근이 불가능하도록 해당 오브젝트 파일 안에서만 링크를 제공해주기 때문에(숨겨줌) 다른 trans unit(오브젝트 파일)이 해당 심볼을 찾을 수 없고, 링크 충돌(심볼 중복)도 일어나지 않는다.
--------------
abc.cpp
const int a = 15; // const 전역변수도 static 상수로 간주된다.
--------------
main.cpp
extern int a; // 외부의 a에 링킹 시도
cout << a << endl; // 확인할 수 없는 외부 참조(기호)
--------------
추가로 const 전역변수도 static 변수로 간주되니 abc.cpp의 a에 링킹할 수 없다.
따라서 static의 internal link로 인해 main()에서 int a 변수를 새로 만들 수 있다.
static = 해당 object file 내에서만 접근이 가능
extern = object file 외부에서도 접근이 가능
extern "C"는 CPP 소스에서 선언한 전역변수나 함수를 C에서 사용해야할 경우에 쓰이는 키워드다.
C++는 컴파일시 네임 맹글링(Name Mangling)을 진행하는데, 맹글링이란 C++에서 지원하는 함수 오버로딩을 구현하기위해 사용되는 심볼 작명 방식이다.
그래서 C++로 작성된 코드는 C언어와 심볼 형식이 일치하지 않아 오류가 발생한다.
(함수 오버로딩 때문에 함수 파라미터에 따라 추가 offset이 심볼에 붙기 때문)
이러한 사태를 방지하기 위해 키워드로 extern "C"를 cpp 함수 앞에 붙이면 네임 맹글링을 진행하지 않고 C 인터페이스를 가진 심볼로 생성한다. (함수 오버로딩 또한 불가능해진다.)
extern "C" void func(int a) {
cout << a << endl;
}
extern "C" void func(int a, int b) { // extern "C"를 사용하면 함수를 오버로드할 수 없다.
cout << a << c << endl;
} // error!