7-2. 정적변수

7-2-가.정적변수

정적변수(Static Variable)는 전역변수와 지역변수의 성격을 동시에 가지는 좀 특별한 기억 부류이다. 앞의 도표에 기록되어 있는 정적변수의 특징들을 살펴보자.

 

선언 위치는 지역변수와 마찬가지로 함수의 선두이다.

통용 범위는 지역변수와 마찬가지로 함수 내부로 국한된다.

저장 장소는 전역변수가 저장되는 정적 데이터 영역이다.

정적 데이터 영역에 저장되므로 프로그램 실행중에 항상 존재한다.

초기값 지정이 없으면 0으로 초기화되고 프로그램 실행시 단 한 번만 초기화된다.

 

정적변수의 성질을 요약하자면 저장 장소는 전역변수이되 통용 범위는 지역변수라 할 수 있다. 정적변수를 선언할 때는 반드시 static이라는 지정자를 붙여야 한다.

 

static int i;

static double d;

 

이렇게 선언하면 i나 d는 정적변수가 되어 정적 데이터 영역에 저장되며 통용 범위는 선언문이 있는 함수 내부로 국한된다. 특정 함수만 사용하되 그 값을 계속 유지할 필요가 있을 때 정적변수를 사용한다. 다음 예제가 정적변수를 사용하는 대표적인 예이다.

 

: Static

#include <Turboc.h>

 

void PrintCount();

 

void main()

{

     int i;

    

     for (i=0;i<5;i++) {

          PrintCount();

     }

}

 

void PrintCount()

{

     static int count=0;

 

     count++;

     printf("저는 %d번째로 호출되었습니다.\n",count);

}

 

PrintCount 함수는 이 함수가 호출된 회수를 화면으로 출력하는데 자신이 호출된 회수를 count라는 변수에 저장하고 있다. 최초 count는 0으로 초기화되고 PrintCount가 한 번 호출될 때마다 1씩 증가하도록 되어 있다. main에서 이 함수를 다섯 번 호출하며 실행 결과는 다음과 같다.

 

저는 1번째로 호출되었습니다.

저는 2번째로 호출되었습니다.

저는 3번째로 호출되었습니다.

저는 4번째로 호출되었습니다.

저는 5번째로 호출되었습니다.

 

count 변수는 PrintCount 함수 내부에서만 사용되므로 이 함수 밖에서는 참조하지 말아야 한다. 하지만 그렇다고 해서 count를 지역변수로 선언해서는 안되는데 지역변수는 함수가 호출될 때마다 스택에 다시 생성되고 함수가 끝날 때 파괴되어 버리므로 값을 계속 유지할 수 없다. 호출 회수를 기억하는 카운트 변수가 제 기능을 다 할 수 없는 것이다. 또 그렇다고 해서 count를 전역변수로 선언하면 PrintCount 이외의 함수에서도 이 변수를 참조할 수 있게 되므로 함수의 독립성을 헤치게 된다.

그래서 기억 장소는 전역적이고 통용 범위는 지역적인 정적변수로 선언한 것이다. 정적변수로 선언하면 함수가 끝나도 값이 파괴되지 않고 계속 유지되므로 카운트로 사용할 수 있다. 또한 통용 범위가 함수 내부로 국한되므로 외부에서 이 변수를 참조할 수 없으며 디버깅시에도 문제가 되지 않고 이 함수를 다른 프로젝트로 가져가 쉽게 재사용할 수도 있다.

보다시피 정적변수는 지역변수와 전역변수의 장점을 모두 가지는 조금 특이한 기억 부류이다. 위 예제처럼 계속 증가하는 카운트 값이나 최후 실행 결과를 함수 스스로가 보유하도록 하고 싶을 때 정적변수를 사용한다. 정적변수를 잘 사용하면 구조적으로도 튼튼하고 디버깅에도 유리한 좋은 코드를 작성할 수 있다.

정적변수에서 한가지 유의할 점은 이 변수가 언제 초기화되는가 하는 점이다. 함수 선두에서 정적변수를 선언하고 있으므로 함수가 호출될 때마다 초기화될 것 같지만 그렇지 않으며 함수가 최초로 호출될 때 단 한 번만 초기화된다. 사실 초기화라는 말 자체에 이미 일회성의 의미가 내포되어 있지 않은가? 위 예에서 count=0;으로 초기화되는 코드가 PrintCount 함수가 호출될 때마다 실행된다면 이 변수가 값을 계속 유지하지 못할 것이다.

또 만약 정적변수 선언문에서 초기화를 하지 않으면 전역변수와 마찬가지로 0으로 자동 초기화된다. 그래서 위 예제의 count 선언문에서 초기화 부분은 빼고 static int count;라고 선언해도 count는 0으로 초기화된다. 함수 내부에서 큰 배열을 선언하고 초기화할 때는 초기화 시간을 절약하기 위해 정적으로 선언하여 한 번만 초기화하도록 해야 한다. 그렇지 않으면 함수가 호출될 때마다 큰 배열이 매번 생성, 초기화, 파괴를 반복하므로 느려진다.

PrintCount 예제에서와 같이 함수 내부에서 선언된 정적변수를 내부 정적변수라고 한다. 정적변수는 그 특성상 특정 함수 전용으로 선언하는 경우가 많기 때문에 보통 정적변수라고 하면 내부 정적변수를 의미한다. 흔하지는 않지만 함수 외부에서도 정적변수를 선언할 수 있는데 이렇게 선언된 변수를 외부 정적변수라고 한다. 외부 정적변수는 특정 함수에 소속되어 있지 않으므로 일반적으로 전역변수와 같은 성질을 가진다.

다만 전역변수와 다른 점은 extern 선언에 의해 외부 모듈로 알려지지 않는다는 점이다. 즉 자신이 선언된 모듈에서만 사용할 수 있는 모듈 전역변수가 된다. 앞의 Extern2 예제에서 global.cpp의 value 변수 선언문을 static int value=1234; 라고 수정하면 global.cpp 외부에서 extern 선언을 하더라도 이 변수를 참조할 수 없게 된다.

전역변수이면서도 외부에 알려서는 안되는 그런 변수가 필요할 때 외부 정적변수를 사용한다. 주로 모듈의 재활용성을 높이기 위해 사용하는데 구체적인 예를 들어 보자. 그래픽 관련 함수들을 제공하는 graphic.cpp와 graphic.h를 아주 공들여서 제작했는데 이 모듈에서 mode, color 같은 전역변수를 사용하고 있다고 하자. 이 모듈을 쓰고 싶은 사람이 graphic.* 파일만 복사해서 사용하면 되도록 하고 싶다.

그런데 이 모듈을 사용하는 프로젝트에 이미 mode, color라는 전역변수가 사용되고 있다면 명칭의 충돌이 발생하게 될 것이고 양쪽 중 하나는 변수의 이름을 바꾸어야 한다. 이럴 때 mode, color 변수를 외부 정적변수로 선언하면 다른 모듈에는 알려지지 않으므로 이름의 충돌을 방지할 수 있게 되고 이 모듈은 아무 프로젝트에서나 재활용하기 쉬워진다.