방송대 프로그래밍언어론 8강: 변수의 영역과 정적·동적 영역 규칙
중첩된 서브프로그램에서 선언하지 않은 이름 price를 사용했다면 어느 변수를 읽을까? 소스 코드의 바깥 블록을 따라갈 수도 있고, 실행 중 자신을 호출한 블록을 거슬러 올라갈 수도 있다. 이름을 찾는 두 지도를 분리해 추적하면 지역·비지역·자유 변수, 영역 구멍과 이름공간을 하나의 판정 규칙으로 연결할 수 있다.
같은 자유 변수도 어떤 지도를 보느냐에 따라 달라진다
학습을 위해 다음과 같이 축약한 프로그램을 가정하자. Show에서 사용하는 price는 그 블록에 선언되어 있지 않은 자유 변수다.
Main: price = 10
Show:
print(price)
Caller: price = 20
call Show
call Caller
정적 영역 규칙은 소스에서 Show를 문맥적으로 둘러싼 블록을 찾는다. Show의 정적 부모는 Main이므로 10을 참조한다. 동적 영역 규칙은 실행 중 호출 관계를 거슬러 올라간다. Show를 호출한 Caller에서 먼저 같은 이름을 만나므로 20을 참조한다.
첫 판단 질문: 자유 변수를 만났다면 이름 자체보다 탐색할 지도를 먼저 고른다. 소스의 중첩 구조를 보면 정적 영역 규칙, 실행 중 호출 사슬을 보면 동적 영역 규칙이다.
영역은 코드의 범위이고 수명은 실행 시간의 구간이다
변수의 영역(scope)은 프로그램에서 그 변수를 사용해 값을 읽거나 쓸 수 있는 범위다. 보통 선언한 지점부터 영역이 시작한다. 반면 수명은 변수가 기억장소와 연결되어 존재하는 시간이다. 지난 강의의 주소 바인딩이 시간축이라면 이번 강의의 영역은 코드 위치축이다.
| 질문 | 영역 | 수명 |
|---|---|---|
| 무엇을 판단하는가? | 이 코드 위치에서 이름을 사용할 수 있는가? | 이 실행 시점에 기억장소가 존재하는가? |
| 주된 축 | 프로그램의 문맥과 위치 | 실행의 시작과 종료 시점 |
| 대표 경계 | 선언 위치와 블록의 끝 | 할당과 해제 |
| 서로 다른 사례 | 지역 이름은 함수 내부에서만 보임 | 호출 중에만 그 지역 기억장소가 살아 있음 |
예를 들어 함수 안에서 선언한 변수는 함수 블록 내부라는 영역을 갖고, 함수가 호출된 동안으로 수명이 한정될 수 있다. 두 범위가 비슷해 보여도 “어디에서 보이는가”와 “언제 존재하는가”는 다른 질문이다.
블록은 영역의 경계를 만드는 언어별 단위다
블록은 프로그램 문장들의 묶음이며, 변수의 영역을 구분하는 단위로 사용된다. 블록 안에서 변수를 선언할 수 있고, 그 선언부터 블록이 끝나는 지점까지가 기본 영역이 된다. 다만 어떤 문장 묶음을 블록으로 인정하는지는 언어마다 다르다.
| 언어 | 강의에서 제시한 블록 단위 | 주의점 |
|---|---|---|
| Algol 60 | begin과 end의 복합문 | 복합문 자체가 블록으로 기능 |
| C·C++·Java | 중괄호 복합문, 함수, 클래스 | 중첩 블록마다 새 선언 가능 |
| Pascal | 주프로그램과 서브프로그램 | begin과 end만으로 된 복합문은 블록이 아님 |
{
int points = 4;
points += 2; /* points 사용 가능 */
}
/* 여기서는 points를 사용할 수 없음 */
이 학습용 C 예제에서 points의 영역은 선언부터 닫는 중괄호까지다. 선언보다 앞에서 사용하거나 블록 밖에서 사용하면 그 이름을 현재 위치의 참조환경에서 찾을 수 없다.
지역·비지역 여부는 현재 위치를 기준으로 다시 정해진다
지역 변수는 현재 블록 안에서 선언되어 사용되는 변수다. 비지역 변수는 현재 위치에서 사용할 수 있지만 현재 블록에서 선언되지는 않은 변수다. 따라서 두 용어는 변수에 영구히 붙는 분류가 아니라 어느 블록에서 보느냐에 따라 달라지는 상대적 관계다.
{
int outer = 7;
/* 지점 A */
{
int middle = 8;
/* 지점 B */
{
int inner = 9;
/* 지점 C */
}
}
}
| 위치 | 지역 변수 | 비지역 변수 | 참조환경 |
|---|---|---|---|
| A | outer | 없음 | outer |
| B | middle | outer | middle, outer |
| C | inner | middle, outer | inner, middle, outer |
참조환경은 프로그램의 한 위치에서 사용할 수 있는 모든 식별자의 모음이다. 현재 위치의 지역 변수뿐 아니라 영역 규칙으로 도달할 수 있는 비지역 변수도 포함한다. 예를 들어 outer는 A에서는 지역 변수지만 B와 C에서는 비지역 변수다.
자유 변수는 선언을 찾으면 비지역 변수가 되고 못 찾으면 오류가 된다
현재 블록에서 선언되지 않았는데 사용하려는 이름을 자유 변수라고 한다. 자유 변수는 아직 참조할 선언이 결정되지 않은 상태를 가리킨다. 영역 규칙에 따라 같은 이름의 선언을 찾으면 비지역 변수로 해석되고, 허용된 탐색 범위 끝까지 찾지 못하면 오류가 된다.
| 현재 블록에서의 상태 | 다음 행동 | 결과 |
|---|---|---|
| 같은 이름의 선언이 있음 | 현재 선언을 선택 | 지역 변수 |
| 선언이 없고 바깥 탐색에서 찾음 | 영역 규칙이 지정한 선언을 선택 | 비지역 변수 |
| 선언이 없고 탐색 끝까지 못 찾음 | 연결할 선언이 없음 | 참조 오류 |
용어 교정: 자유 변수와 비지역 변수는 완전히 같은 말이 아니다. 자유 변수는 현재 블록에 선언이 없어서 탐색이 필요한 이름이고, 비지역 변수는 탐색에 성공해 바깥 선언과 연결된 결과다.
정적 영역 규칙은 소스의 정적 부모를 반복해서 찾는다
정적 영역 규칙은 블록들의 정적 내포 관계, 즉 소스 코드의 문맥적인 포함 관계로 참조 위치를 결정한다. 현재 블록을 직접 둘러싼 가장 가까운 블록이 정적 부모이고, 현재 블록을 문맥적으로 둘러싼 모든 블록이 정적 조상이다. 실행하지 않아도 구조를 알 수 있어 어휘적 영역 규칙이라고도 한다.
- 현재 블록에서 같은 이름의 선언을 찾는다. 있으면 지역 변수다.
- 없으면 현재 블록의 정적 부모에서 찾는다.
- 찾으면 그 선언을 비지역 변수로 선택하고 멈춘다.
- 없으면 다음 정적 부모로 올라가며 반복한다.
- 최외곽 영역까지 찾지 못하면 오류다.
이 규칙에서 서브프로그램을 누가 호출했는지는 중요하지 않다. 소스에서 어디에 선언되어 있는지가 탐색 경로를 고정한다. 그래서 강의의 비교 기준에서는 컴파일 시점에 참조 위치를 결정하고 정적 타입 검사를 수행할 수 있으며, 실행 중 이름 탐색 부담이 작다.
동적 영역 규칙은 실행 중 호출자를 거슬러 올라간다
동적 영역 규칙은 서브프로그램의 호출 관계를 동적 내포 관계로 사용한다. 호출 사슬은 실제 실행 경로에 따라 달라지므로 프로그램 수행 시점에만 참조 위치를 결정할 수 있다.
- 현재 블록에서 같은 이름의 선언을 찾는다. 있으면 지역 변수다.
- 없으면 현재 블록을 호출한 블록에서 찾는다.
- 찾으면 그 선언을 비지역 변수로 선택하고 멈춘다.
- 없으면 그 호출자를 호출한 블록으로 올라가며 반복한다.
- 최초 호출자의 영역까지 찾지 못하면 오류다.
첫 섹션의 예에서 호출 사슬은 Main → Caller → Show다. Show의 자유 변수 price를 찾을 때 바로 앞 호출자인 Caller에서 선언을 만나므로 20과 연결된다. 호출 경로가 달라지면 같은 소스의 자유 변수가 다른 선언과 연결될 여지도 있다.
강의의 비교 맥락: 동적 영역 규칙은 수행 시점에 참조 위치가 정해져 정적 타입 검사가 어렵고 이름 탐색 비용이 커진다. 강의는 LISP 등 인터프리터 방식의 구현 언어를 적용 사례로 제시한다.
두 탐색 규칙은 같은 프로그램에서 서로 다른 답을 낸다
첫 사례를 두 개의 목록으로 바꾸면 차이가 더 선명하다.
| 탐색 단계 | 정적 영역 규칙 | 동적 영역 규칙 |
|---|---|---|
| 시작 | Show의 현재 블록 | Show의 현재 블록 |
| 첫 이동 | 소스상 정적 부모 Main | 실행 중 호출자 Caller |
| 처음 만난 선언 | Main.price = 10 | Caller.price = 20 |
| 결과 | 10 출력 | 20 출력 |
| 결정 가능 시점 | 컴파일 시점 | 프로그램 수행 시점 |
만약 Show를 소스에서 Caller 내부에 선언하도록 구조를 바꾸면 정적 부모도 Caller가 되어 정적 규칙의 결과 역시 20이 된다. 반대로 호출자 Caller에 price 선언이 없다면 동적 규칙은 그다음 호출자 Main까지 올라가 10을 찾는다. 두 규칙의 결과는 우연히 같을 수 있지만 탐색 근거는 여전히 다르다.
같은 이름의 가까운 선언은 바깥 영역에 구멍을 만든다
영역 구멍(scope hole)은 비지역 변수가 같은 이름의 지역 변수 때문에 보이지 않게 되는 영역이다. 다음 학습용 예에서 안쪽 quota가 보이는 동안 바깥 quota는 일반 이름으로 접근할 수 없다.
int quota = 10;
int main() {
int quota = 30;
quota++; // 지역 quota: 31
::quota++; // 전역 quota: 11
}
가장 가까운 선언이 먼저 선택되므로 지역 quota가 전역 이름을 가린다. C++의 ::quota처럼 앞에 영역 연산자를 붙이면 전역 선언을 명시적으로 선택할 수 있다. 영역 구멍은 바깥 변수가 사라지거나 수명이 끝났다는 뜻이 아니라, 특정 코드 범위에서 일반 이름 찾기로 보이지 않는다는 뜻이다.
교정 기준: 같은 이름이 여러 번 선언되면 가장 가까운 선언부터 찾고, 가려진 바깥 이름이 꼭 필요할 때만 언어가 제공하는 한정 방법을 사용한다.
이름공간은 관련 이름을 묶고 한정 이름으로 충돌을 푼다
전역 변수는 어떤 블록에도 포함되지 않는 곳에서 선언되어 프로그램 전체를 영역으로 갖고, 각 블록에서는 비지역 변수로 취급된다. 전역 이름과 같은 지역 이름을 선언하면 영역 구멍이 생긴다.
이름공간(namespace)은 관련성이 높은 변수와 함수를 하나의 이름 있는 영역으로 관리한다. C++에서는 이름공간 이름과 영역 연산자를 붙여 특정 이름을 정확히 선택하거나, using 지시문으로 한정하지 않은 이름을 현재 문맥에서 사용할 수 있게 한다.
namespace Metrics {
int count = 0;
void add() { count++; }
}
int main() {
int count = 5;
count++; // 지역 count: 6
Metrics::count++; // 이름공간 count: 1
Metrics::add(); // 이름공간 count: 2
}
이 학습용 예에서 Metrics::count는 지역 count와 이름이 같아도 충돌하지 않는다. 강의의 using namespace 방식은 이름을 짧게 쓸 수 있게 하지만, 지역 이름과 다시 겹치면 어느 선언을 뜻하는지 확인해야 한다. 충돌 가능성이 있는 지점에서는 한정 이름이 참조 대상을 더 분명하게 드러낸다.
이름 참조 문제는 다섯 단계로 해결한다
새로운 중첩 코드에서 어떤 변수를 참조하는지 묻는다면 다음 순서를 그대로 적용한다.
- 현재 위치 표시: 이름이 사용된 블록과 선언 위치를 코드에 표시한다.
- 지역 선언 검사: 현재 블록에 같은 이름의 선언이 있으면 즉시 선택한다.
- 자유 변수 판정: 현재 선언이 없다면 자유 변수로 표시한다.
- 지도 선택: 정적 규칙이면 정적 부모, 동적 규칙이면 실행 중 호출자를 차례로 추적한다.
- 가장 먼저 찾은 선언 확정: 찾으면 비지역 변수, 끝까지 없으면 오류로 판정한다. 같은 이름 때문에 가려졌다면 영역 연산자나 이름공간 한정을 검토한다.
마지막으로 결과만 쓰지 말고 “정적 부모에서 처음 만난 선언” 또는 “직전 호출자에서 처음 만난 선언”처럼 경로를 함께 적는다. 이 습관은 정적·동적 규칙의 결과가 우연히 같을 때도 근거를 구분하게 해 준다.
핵심 개념 정리
- 영역은 이름을 사용할 수 있는 코드 범위이고, 수명은 기억장소가 존재하는 실행 시간이다.
- 참조환경은 한 위치에서 사용할 수 있는 지역·비지역 식별자의 전체 모음이다.
- 자유 변수는 현재 블록에 선언이 없는 이름이며, 영역 규칙으로 선언을 찾으면 비지역 변수가 된다.
- 정적 영역 규칙은 소스의 정적 부모를, 동적 영역 규칙은 실행 중 호출자를 거슬러 올라간다.
- 가까운 같은 이름은 바깥 영역에 구멍을 만들며, 영역 연산자와 이름공간 한정은 가려진 대상을 명시한다.
이름 참조를 풀 때는 먼저 현재 블록의 선언을 찾고, 없다면 자유 변수로 표시한다. 다음으로 문제에서 요구한 지도가 소스의 중첩 구조인지 실행의 호출 사슬인지 결정한다. 그 지도에서 처음 만난 선언이 참조 대상이며, 찾지 못하면 오류다. 같은 이름이 가리고 있다면 수명 문제가 아니라 영역 구멍인지 확인한 뒤 한정 이름으로 대상을 분명히 한다.
예상문제 10선
1. 변수의 영역을 가장 정확하게 설명한 것은?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ②
- ① 오답: 기억장소가 존재하는 시간은 변수의 수명이다.
- ② 정답: 영역은 특정 코드 위치에서 그 이름으로 접근할 수 있는지를 나타낸다.
- ③ 오답: 타입의 크기는 구현 시 바인딩되는 타입 속성이며 이름의 사용 범위가 아니다.
- ④ 오답: 대입 횟수나 값의 유지와 영역의 경계는 별개의 문제다.
2. 자유 변수와 비지역 변수의 관계를 옳게 설명한 것은?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ③
- ① 오답: 자유 변수는 현재 블록에서 선언되지 않은 사용 이름이며 전역 선언으로 한정되지 않는다.
- ② 오답: 두 분류는 수명이나 할당 상태가 아니라 선언과 참조 위치의 관계를 다룬다.
- ③ 정답: 영역 규칙의 탐색에 성공해야 자유 변수의 참조 대상이 비지역 변수로 확정된다.
- ④ 오답: 선언을 끝까지 못 찾은 자유 변수는 오류가 되므로 두 용어를 동일시할 수 없다.
3. 정적 영역 규칙으로 자유 변수의 선언을 찾는 올바른 순서는?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ①
- ① 정답: 현재 선언을 먼저 확인하고 소스상 가장 가까운 포함 블록부터 바깥으로 이동한다.
- ② 오답: 호출자를 거슬러 올라가는 순서는 동적 영역 규칙에 해당한다.
- ③ 오답: 저장 위치의 종류는 정적 내포 관계를 탐색하는 순서가 아니다.
- ④ 오답: 가까운 선언을 우선해야 하므로 최외곽이나 형제 블록부터 찾지 않는다.
4. 본문의 Main → Caller → Show 사례를 동적 영역 규칙으로 실행할 때 Show의 price가 참조하는 값은?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ④
- ① 오답: 소스상 정적 부모를 따를 때의 결과이며 동적 규칙의 탐색 기준이 아니다.
- ② 오답: 현재 선언이 없으면 호출자를 계속 찾고, 탐색 끝까지 없을 때만 오류다.
- ③ 오답: 영역 규칙은 가장 먼저 찾은 선언 하나를 선택하며 값을 결합하지 않는다.
- ④ 정답: 동적 규칙은 Show를 직접 호출한 Caller부터 검사한다.
5. “영역 구멍이 생기면 가려진 바깥 변수의 수명이 끝난다”라는 설명의 오류는?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ③
- ① 오답: 영역 구멍은 저장 위치 이동이 아니라 가까운 같은 이름이 바깥 이름을 가리는 현상이다.
- ② 오답: 호출 순서가 아니라 특정 범위에서 이름이 보이는지가 핵심이다.
- ③ 정답: 바깥 변수는 살아 있어도 일반 이름 찾기에서는 가려질 수 있다.
- ④ 오답: 서로 다른 중첩 블록에는 같은 이름을 선언할 수 있으며 그때 영역 구멍이 생긴다.
6. 정적 영역 규칙과 동적 영역 규칙의 결정적 차이를 옳게 짝지은 것은?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ②
- ① 오답: 두 규칙 모두 이름의 참조 선언을 찾으며 값 크기나 주소 추적을 기준으로 나누지 않는다.
- ② 정답: 하나는 소스 구조, 다른 하나는 실제 호출 사슬이라는 서로 다른 지도를 사용한다.
- ③ 오답: 두 규칙 모두 현재 지역 선언을 먼저 찾고 필요하면 비지역 선언을 찾는다.
- ④ 오답: 이름공간과 영역 연산자는 이름 충돌을 관리하는 장치이지 두 영역 규칙의 정의가 아니다.
7. 중첩 코드의 가장 안쪽 지점에서 inner는 그 블록에 선언되고, middle과 outer는 바깥 블록에 선언되어 있다. 참조환경은?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ④
- ① 오답: 참조환경은 지역 변수뿐 아니라 현재 위치에서 보이는 비지역 변수도 포함한다.
- ② 오답: 현재 블록의 지역 변수 inner도 사용할 수 있으므로 빠뜨릴 수 없다.
- ③ 오답: 가까운 바깥 블록의 middle과 현재 블록의 inner도 보인다.
- ④ 정답: 현재 위치에서 사용 가능한 지역·비지역 식별자의 전체 모음이다.
8. 본문의 C++ 예에서 전역 quota는 10, 지역 quota는 30이다. quota++;와 ::quota++; 실행 후 두 값은?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ①
- ① 정답: 한정 없는 이름은 가까운 지역 변수를, ::가 붙은 이름은 전역 변수를 각각 한 번 증가시킨다.
- ② 오답: 두 증가 연산은 같은 지역 변수가 아니라 서로 다른 선언에 적용된다.
- ③ 오답: 일반 quota는 영역 구멍 안에서 지역 선언을 선택한다.
- ④ 오답: 두 값을 더하거나 전역 값을 초기화하는 연산은 없다.
9. 이름공간의 주된 역할로 옳은 것은?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ③
- ① 오답: 이름공간은 이름의 조직과 구분을 돕지만 변수 수명을 일괄 연장하지 않는다.
- ② 오답: 자유 변수의 참조 위치 결정과 주소 복사는 서로 다른 문제다.
- ③ 정답: 영역 자체의 이름을 이용해 관련 이름을 묶고 한정해 사용할 수 있다.
- ④ 오답: 이름공간은 영역 규칙의 탐색 지도를 바꾸는 변환기가 아니다.
10. 중첩 서브프로그램의 자유 변수 rate가 어느 선언을 참조하는지 해결하는 절차로 가장 적절한 것은?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ④
- ① 오답: 영역 규칙은 값의 크기를 비교하지 않고 선언 탐색 순서로 대상을 정한다.
- ② 오답: 수명은 기억장소의 시간 범위이며 이름 탐색 우선순위가 아니다.
- ③ 오답: 현재 블록의 지역 선언이 가장 먼저이며 바깥 선언은 그다음이다.
- ④ 정답: 지역 검사 후 정적 부모 또는 호출자를 따라 처음 찾은 선언으로 참조를 확정한다.
참고 자료와 작성 기준
이 글은 해당 차시 강의자료를 바탕으로 학습 목적에 맞게 재구성한 비공식 학습자료입니다. 이름 탐색 지도, 참조환경 추적, 영역 구멍 코드와 문제 해설은 학습자의 이해를 돕도록 구성하고 검토했습니다.
- 작성·편집: 올에이클래스 학습연구팀
- 주요 근거: 한국방송통신대학교 컴퓨터과학과 프로그래밍언어론 제8강 「변수의 영역」 강의자료(2025)
- 보충 자료: 별도의 외부 자료를 사용하지 않음
- 편집 원칙: 올에이클래스 편집 정책
- 최종 내용 검토: 2026-08-29
댓글
댓글 쓰기