전역 변수를 지역변수로 만들면? (static과 extern에 대한 이야기)

전역 변수를 지역적으로 만들면?




간단할 것 같은 위 물음에 답을 찾으려면 

생각보다 많은 것에 대해 알고 있어야 한다. 



긴 여정을 시작해 보자.




2015/06/18 - [프로그래밍/C언어] - 지역변수와 전역변수와 정적변수[static, extern, auto, register]


번역단위

 전역 변수를 지역 변수으로 만들면 통용 범위가 모듈단위로 줄어든다. 모듈의 의미는 여러가지나 여기서 말하는 모듈이라 함은 하나의 cpp파일과 이 cpp파일이 포함하는 헤더파일(*.h)파일을 말한다. 이는 컴파일러가 컴파일을 하는 번역 단위다.(번역단위란, 하나의 목적파일(*.o)을 만드는데 필요한 소스파일을 말한다.)

(참고 : a.cpp, a.h, main,cpp 가있고 a.cpp와 main.cpp는 각각 a.h를 include 한다.

각 번역단위(모듈)는 (a.h, a.cpp), (a.h. main.cpp) 로 2개가 되고 각각 목적파일 (a.o), (main.o)를 만들게 된다.

 

 

헤더파일에는 함수 선언만.

이 부분도 extern에 관한 내용인데 extern에 관한 내용은 항상 C와 C++을 구별하여 생각해야 한다.

우리가 흔히 하는 함수의 원형 선언은 사실은 extern이 생략된 것이다.(무조건 extern)

int fun(); // extern int fun();

int main() {

fun();… }

int fun() {

… }

C++에서 조차 변수의 경우와는 다르게 extern을 사용하지 않아도 extern으로 처리한다.  이런 성질 덕에 헤더파일에 함수의 원형 선언을 해도 문제가 생기지 않는다. extern 선언은 여러 번 해도 상관이 없다.

extern int b;

extern int b;

..

따라서 다음과 같이 컴파일 할 경우 a.h가 각 목적 파일에 동일하게 포함된다. 하지만 extern은 경우 중복 선언을 허용하기 때문에 문제가 생기지 않는다. 변수의 경우는 보시다시피 때에 따라서 달라지게 된다. 링크 시 에러를 차단하는 방법은 extern지정자를 사용하는 경우만 헤더파일에서 사용하면 된다. 만약 단순히 int a;라는 변수를 선언 또는 정의 하면 목적파일은 만들어지나, 링크 시 두 번 선언 또는 두 번 정의했다는 에러가 발생하게 된다.

 

   

이런 에러를 피하는 가장 좋은 해결책은 헤더파일에 함수선언만 하는 것이다.

 

이름 충돌

위에서 말한 문제를 해결하는 방법은 여러 가지가 있다. 위 그림을 보면 main.cpp이 포함하는 a.h가 있다. 이 파일에 int a=3;이라는 변수가 전역으로 선언, 정의 되어 있다고 가정하자.(실제로 이런 코드는 자제하자) 목적파일은 잘 만들어진다. 그런데 링크 시 에러가 발생하고, 에러의 원인은 a라는 전역변수가 프로그램 내에서 두 번 중복되기 때문이다. 이를 피하는 방법이 있다. 이 글의 주제인 전역 변수를 지역변수로 만드는 것이다. 방법은 간단히 static만 붙여 주면 된다.

 

static은 전역적 성질을 지역적으로 만든다.

하나의 번역단위는 하나의 지역이라고 볼 수 있다. 기본적으로 변수를 함수외부에 선언하면 전역적 특징을 갖는다. 여기에 static이 붙으면 하나의 번역단위(목적파일) 내에서만 사용하는 지역 변수가 된다. 따라서 main.cpp와 a.cpp는 같은 a.h에서 static int a=3;을 include하지만 static이기 때문에 각 cpp파일에서 사용하는 a는 각각의 지역변수가 되어 따로따로 존재하게 된다.

a의 본래의 사용목적과 다르게 동작할 듯 보이지만 우선 static으로 만들어 급한 불을 껐다. 사실 각 cpp파일에서 동일한 a를 사용할 목적이라면 a.cpp에 int a=3; 이라고 전역으로 정의하고 , 헤더파일에서 extern int a; 라고 선언 하는 것이 적절하다. 단지, 여기서 든 예는 static의 지역적 특성을 설명을 위한 예이다.

그럼 문제를 확장해보자. 아래 그림과 같이 b.cpp, b.h를 추가한다. 그리고 위와 동일하게a.h와 b.h에 int a=3;을 정의하여 충돌하게 만든다.

a.cpp 모듈과 b.cpp 모듈의 목적파일은 각각 생성될 것이다. 그런에 main.o는 생성되지 않고, 위 예와 달리 링크전에 에러가 발생한다.

그림을 보면 main.cpp 가 a.h와 b.h를 포함하고, int a=3;을 중복 정의 했기 때문에 컴파일 에러가 발생한다.

그럼 이경우 static을 사용하면? 당연히 안 된다. static int a=3;이라는 구문이 어쨌든 두번 중복 정의된다. 위와 똑 같은 문제로 컴파일 에러가 난다.

모로가도 에러가 나는 이름 충돌이기 때문에 헤더파일에서는 가능하면 변수 정의는 자제하는 것이다.

 

 

해결법

이에 대한 해결방법은 변수의 사용방법에 따라 다르다.

①a라는 변수명을 각각의 모듈에서 그대로 써야 하고 각각의 모듈에서 지역적으로 사용하고 싶다면 각각의 cpp파일에 변수를 선언하고 static지정자를 붙여주면 된다.

②a라는 변수명을 모든 모듈에서 공유하고 싶다면 세계의 모듈 중 아무 모듈에서 a를 전역변수로 선언한다. 사용하고자 하는 모듈에서 extern int a;라고 선언해주고 사용하면 a를 전역변수로서 공유하면 된다.

③a라는 변수명을 두개의 모듈에서는 공유하고 싶고, 나머지 하나의 모듈에서는 자신만의 지역변수로 사용하고 싶다면, 공유하고 싶은 모듈 중 하나의 모듈에 a를 전역으로 선언 한다. 공유할 나머지 모듈에서 extern으로 선언한다. 그리고 a를 자신만의 지역변수로 사용할 모듈에서는 static으로 선언하면 된다.


2015/06/20 - [프로그래밍/C언어] - 헤더파일에 static 선언을 하면 안 되는 이유


위 그림같이 3개의 번역단위로 3개의 목적파일이 생성된다. 각각의 목적파일을 만들 때는 외부에 어떤 전역 변수가 있는지 extern으로 지정하지 않는 한 알지 못한다. 외부 전역변수를 사용하려면 각각의 번역 단위에서 extern을 사용해야 한다.

또한 외부 전역변수를 알지 못하기 목적파일은 생성되어도 링크 시 외부 전역변수와 모듈 내에서 선언한 전역변수가 이름충돌이 일어날 수 있다.

가장 좋은 건 extern을 사용하지 않는 것이지만, extern을 사용함으로 편리하고 효율적일 때도 있을 수 있다.

   

간혹 제 블로그 글을 아무런 허락도 없이 전체 또는 부분적으로 발췌하여 올리는 분들이 계신데, 제 블로그 글들은 대부분이 저의 노력으로 작성된 글이고 글 하나 작성하기 위해서는 길게는 몇 일이 걸린 글들도 많습니다. 무단으로 전체 또는 부분을 퍼가시지 마시기 바랍니다.. 

이 댓글을 비밀 댓글로
    • 2019.09.23 00:47
    비밀댓글입니다