7-3-나.블록 범위

앞에서 지역변수란 함수 내부에서 선언된 변수라고 했는데 이는 어디까지나 편의상 이해하기 쉽도록 설명한 것이다. 이제 통용 범위까지 살펴 봤으므로 지역변수를 좀 더 정확하게 정의해 보도록 하자. 지역변수는 { } 괄호안의 블록에 선언된 변수를 의미하며 변수가 선언된 블록 내부에서만 통용된다. { } 괄호안에서만 통용되는 범위를 블록 범위라고 하는데 { } 괄호가 보통 함수의 시작과 끝을 나타내고 함수 선두에 지역변수를 선언하는 경우가 많기 때문에 지역변수의 통용 범위는 함수 내부가 되는 것이다.

지역변수의 통용 범위는 정확하게 함수 내부가 아니고 선언된 블록의 내부이다. 그래서 함수 내부안에 { } 블록을 만들고 이 안에 또 다른 지역변수를 만들 수도 있다. 이렇게 블록 내부에서 선언된 변수는 블록 바깥에서는 사용할 수 없다. 다음 예제를 보자.

 

: BlockScope

#include <Turboc.h>

 

void main()

{

     if (1==1) {

          int i;

          i=5;

     }

     i=10;        // 에러

}

 

main 함수 내부에 if 블록이 있는데 이 블록 내부에서 int i를 선언했다. if 블록이 시작되는 {  다음에 i를 선언했으므로 i는 if 블록 내부에서만 통용되는 지역변수이며 그래서 if 블록 바깥으로는 i가 알려지지 않는다. 만약 i를 main 함수가 시작되는 { 다음에, 그러니까 if문 이전에 선언했다면 이 변수는 main 함수의 지역변수가 되겠지만 if 블록 안에 선언되었기 때문에 통용 범위가 main 함수 범위보다도 더 좁아진다.

함수의 범위보다도 더 좁은 이런 통용 범위를 블록 범위라고 한다. 만약 이미 선언된 변수명을 다시 사용하고 싶다면 일부러 블록을 하나 만들고 이 블록안에 원하는 변수를 선언하면 된다. 다음이 그 예이다.

 

: BlockScope2

#include <Turboc.h>

 

void main()

{

     int i;                           // 함수 범위

     i=5;

     {

          int i;                       // 블록 범위

          i=3;

          printf("i=%d\n",i);

     }

     printf("i=%d\n",i);

}

 

main 함수 선두에 선언된 i는 함수 범위를 가지는 지역변수이고 { } 안에 선언된 i는 블록 범위를 가지는 지역변수이다. 두 변수는 이름이 같지만 완전히 다른 변수이며 각각 다른 값을 저장할 수 있다. 이때 블록 내에서는 안쪽의 i에 의해 바깥의 i가 가려지는데 블록 내에서 바깥쪽의 i를 참조하는 방법은 제공되지 않는다. 이런 문법을 처음 본 사람은 C++에서 새로 추가된 문법으로 생각되겠지만 이 문법은 원래 C부터 있었던 기능이다.

블록 범위의 변수를 설명하기 위해 의도적으로 예제를 만들다 보니 조금 어색한 감이 있기는 하다. 사실 같은 이름의 변수를 재사용하기 위해 일부로 블록을 만들어 쓰는 것보다는 차라리 다른 이름의 변수를 쓰는 것이 더 좋을 것이다. 하지만 함수가 아주 길 때 for 문 내부에서만 또는 case문의 내부에서만 사용할 지역변수가 필요하다면 블록 범위의 변수가 유용하게 사용된다.

블록 범위 변수의 특수한 예로 for문의 초기식에 선언되는 변수가 있다. 일정 회수를 반복하는 for문은 보통 제어 변수를 필요로 하는데 이 제어 변수를 초기식에서 선언할 수 있다면 무척 편리할 것이다. 다음 예제를 보자.

 

: ForBlock

#include <Turboc.h>

 

void main()

{

     int i;

 

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

          printf("%d\n",i);

     }

     for (int i=5;i<9;i++) {

          printf("%d\n",i);

     }

}

 

main 함수의 지역변수로 i가 선언되어 있고 두 개의 for문에서 각각 또 다른 i를 선언하고 있는데 이렇게 하더라도 각각의 i는 범위가 달라 별 문제가 없다. 루프의 제어 변수로 잠시만 사용하고 버릴 변수라면 이렇게 선언해서 쓰는 것이 편리하다. 그러나 모든 컴파일러가 이런 블록 범위 변수를 지원하는 것은 아니다.

이 예제를 비주얼 C++ 6.0으로 컴파일해 보면 같은 변수를 두 번이나 중복 선언했다는 에러가 두 개 출력되는데 이는 for문의 초기식에 선언된 변수를 블록 범위로 인정하지 않는다는 뜻이다. 비주얼 C++ 6.0은 for문의 초기식에 변수를 선언하는 것은 허용하지만 이 변수가 별도의 범위를 갖지는 않으며 for문이 끝난 후에도 존재하기 때문에 for문을 포함하는 바깥 블록 내에서 이 변수가 유일해야 한다는 제약이 있다.

반면 비주얼 C++ 7.0이나 Dev-C++로 이 예제를 컴파일해 보면 아무런 문제없이 컴파일되며 실행도 잘 된다. 이 두 컴파일러는 블록 범위의 새로운 변수를 선언하는 것을 허용하며 for문을 벗어날 때 이 변수를 파괴하므로 새로운 for 루프에서 똑같은 이름의 제어 변수를 또 선언할 수 있다. 그래서 각 루프는 고유의 제어 변수를 가질 수 있어 무척 편리하다.

그렇다면 도대체 어떤 컴파일러가 틀린 것인지를 판정해 봐야 하는데 14882 표준 문서의 97페이지를 보면 for의 초기식에 선언된 변수의 범위는 for문에 국한된다고 분명히 명시되어 있다. 결국 비주얼 C++ 6.0은 틀린 컴파일을 하고 있는 것이며 비주얼 C++ 7.0에서 이 문제를 해결했다. Dev-C++은 표준대로 컴파일러를 잘 만들었다고 할 수 있다.

위 예제는 C++ 표준 문법에 맞게 잘 작성했으므로 앞으로는 이런 제어 변수 선언문을 사용할 수 있을 것이다. 그러나 비주얼 C++ 6.0이 이런 문법을 지원하지 않기 때문에 당분간은 이 문법을 사용할 수 없으며 어쩌면 앞으로 상당한 기간동안 쓸 수 없을 것이다. 왜냐하면 비주얼 C++ 6.0의 위치는 그렇게 쉽게 무시할 수 없는 중요한 의미를 가지기 때문에 설사 표준에 맞다 하더라도 이 컴파일러에서 컴파일되지 않는다면 문제가 심각하기 때문이다.