변수 영역
"오늘의AI위키"의 AI를 통해 더욱 풍부하고 폭넓은 지식 경험을 누리세요.
1. 개요
변수 영역은 변수의 유효 범위를 나타내는 개념으로, 전역, 모듈, 파일, 함수, 블록, 표현식 등 다양한 종류가 있다. 전역 영역은 소스 코드 전체에서 사용 가능하며, 모듈 영역은 모듈 내에서만, 파일 영역은 파일 내에서만 변수를 사용할 수 있게 한다. 함수 영역은 함수 내에서, 블록 영역은 블록 내에서만 변수의 유효 범위를 제한한다. 표현식 영역은 특정 표현식 내에서만 변수를 사용할 수 있다. 변수 영역은 정적 영역 규칙과 동적 영역 규칙으로 구분되며, 대부분의 현대 프로그래밍 언어는 정적 영역 규칙을 사용한다. 객체 지향 프로그래밍에서는 인스턴스 영역과 클래스 영역이 존재하며, 한국 개발 환경에서는 코드의 가독성, 유지보수성, 재사용성을 위해 변수 영역을 신중하게 관리하는 것이 중요하다.
더 읽어볼만한 페이지
변수 영역 |
---|
2. 종류
변수 영역은 이름이 프로그램의 어느 부분에서 유효한지에 따라 다양한 종류로 나뉜다.
이름(식별자)의 범위는 1960년 ALGOL 60 명세에서 정의된 이후 거의 변경되지 않았다.[1]
- ALGOL 60 (1960): 단순 변수, 배열, 레이블, 스위치 및 프로시저와 같은 양의 범위는 해당 양과 관련된 식별자의 선언이 유효한 문과 표현식의 집합이다.[1]
- C (2007): 식별자는 객체, 함수, 구조체, 공용체 또는 열거형의 태그 또는 멤버, typedef 이름, 레이블 이름, 매크로 이름 또는 매크로 매개변수를 나타낼 수 있다. 동일한 식별자가 프로그램의 다른 지점에서 다른 개체를 나타낼 수 있으며, 식별자는 해당 ''범위'' 내에서만 ''보이는''(사용 가능한) 상태가 된다.[2]
- Go (2013): 선언은 식별자를 상수, 타입, 변수, 함수, 레이블 또는 패키지에 바인딩한다. 선언된 식별자의 범위는 해당 식별자가 지정된 상수, 타입, 변수, 함수, 레이블 또는 패키지를 나타내는 소스 텍스트의 범위이다.[3]
"범위"는 주어진 이름이 주어진 변수를 참조할 수 있는 시점, 즉 선언이 효력을 갖는 시점을 의미하며, 함수, 타입, 클래스, 레이블, 상수 및 열거형과 같은 다른 개체에도 적용될 수 있다.[1][2][3]
범위를 논의할 때, ''범위'', ''확장'', ''컨텍스트''라는 세 가지 기본 개념이 있다. "범위"와 "컨텍스트"는 자주 혼동된다. 범위는 이름 바인딩의 속성이며, 컨텍스트는 프로그램의 일부 속성으로, 소스 코드의 일부(''어휘 컨텍스트'' 또는 ''정적 컨텍스트'') 또는 실행 시간의 일부(''실행 컨텍스트'', ''런타임 컨텍스트'', ''호출 컨텍스트'' 또는 ''동적 컨텍스트'')이다. 실행 컨텍스트는 어휘 컨텍스트와 콜 스택과 같은 추가 런타임 상태로 구성된다.
범위는 소스 코드 수준의 개념이며, 이름 바인딩, 특히 변수 또는 함수 이름 바인딩의 속성이다. 소스 코드의 이름은 프로그램의 엔티티에 대한 참조이며, 언어의 컴파일러 또는 인터프리터의 동작의 일부이다. 범위의 문제는 포인터와 유사하다. 이름이 컨텍스트 내에 있지만 변수가 초기화되지 않은 경우 변수의 값을 사용하는 것은 와일드 포인터를 역참조하는 것과 유사하며 정의되지 않는다. 변수는 컨텍스트에서 벗어날 때까지 파괴되지 않으므로, 댕글링 포인터와 유사한 것은 존재하지 않는다.
변수와 같은 엔티티의 경우, 범위는 수명 (또는 확장)의 부분 집합이다. 이름은 존재하는 변수를 참조할 수 있지만 (값이 정의되지 않은 상태일 수 있음), 존재하는 변수가 반드시 표시되는 것은 아니다. 변수가 존재하지만 액세스할 수 없거나 액세스할 수 있지만 주어진 이름을 통해 액세스할 수 없는 경우, 이는 컨텍스트에 있지 않다 (프로그램이 "이름의 범위를 벗어남"). 다른 경우에는 "수명"이 관련이 없다. 레이블 (소스 코드의 명명된 위치)은 프로그램과 동일한 수명을 가지지만 (정적으로 컴파일된 언어의 경우), 프로그램의 주어진 시점에서 컨텍스트 내에 있거나 없을 수 있으며, 정적 변수의 경우에도 마찬가지이다. 정적 전역 변수는 전체 프로그램에 대해 컨텍스트 내에 있는 반면, 정적 지역 변수는 함수 또는 다른 지역 컨텍스트 내에서만 컨텍스트 내에 있지만, 둘 다 전체 프로그램 실행의 수명을 갖는다.
이름이 어떤 엔티티를 참조하는지 결정하는 것은 이름 확인 또는 이름 바인딩이라고 하며 언어마다 다르다. 이름이 주어지면, 언어는 일치하는 항목에 대해 컨텍스트 내에 있는 모든 엔티티를 확인한다. 모호한 경우 (전역 변수와 동일한 이름을 가진 지역 변수와 같이 동일한 이름을 가진 두 개의 엔티티) 이름 확인 규칙을 사용하여 이를 구별한다. 가장 자주, 이름 확인은 "내부에서 외부 컨텍스트" 규칙에 의존한다. 예를 들어 Python LEGB (지역, 묶는, 전역, 내장) 규칙은 이름은 묵시적으로 가장 좁은 관련 컨텍스트로 확인된다. 경우에 따라 이름 확인을 명시적으로 지정할 수 있다 (예: Python의 `global` 및 `nonlocal` 키워드 사용). 다른 경우에는 기본 규칙을 재정의할 수 없다.
두 개의 동일한 이름이 동시에 다른 엔티티를 참조하는 컨텍스트 내에 있는 경우, 더 높은 우선 순위의 이름 (일반적으로 가장 안쪽)이 더 낮은 우선 순위의 이름을 "마스킹"하는 ''이름 마스킹''이 발생한다고 한다. 변수 수준에서, 이것은 변수 섀도잉이라고 한다. 마스킹으로 인한 논리 오류의 가능성으로 인해, 일부 언어는 마스킹을 허용하지 않거나 권장하지 않으며, 컴파일 시간 또는 런타임에 오류 또는 경고를 발생시킨다.
다양한 프로그래밍 언어는 다양한 종류의 선언과 이름에 대해 다양한 범위 규칙을 가지고 있다. 이러한 범위 규칙은 언어 의미론에 큰 영향을 미치며, 결과적으로 프로그램의 동작과 정확성에 영향을 미친다. C++과 같은 언어에서, 바인딩되지 않은 변수에 액세스하는 것은 잘 정의된 의미를 가지지 않으며 정의되지 않은 동작을 초래할 수 있다, 댕글링 포인터를 참조하는 것과 유사하다. 범위 밖에서 사용된 선언 또는 이름은 구문 오류를 생성한다.
범위는 다른 언어 구성 요소에 자주 묶여 있으며 암시적으로 결정되지만, 많은 언어는 범위를 제어하기 위한 구성 요소를 특별히 제공한다. 스코프는 단일 표현식에서 전체 프로그램에 이르기까지 다양하게 적용될 수 있다. 가장 간단한 스코프 규칙은 전역 스코프로, 모든 엔티티가 전체 프로그램에서 보인다. 기본적인 모듈형 스코프 규칙은 2단계 스코프로, 프로그램의 모든 곳에서 전역 스코프를 가지며, 함수 내에서는 지역 스코프를 갖는다. 더 정교한 모듈형 프로그래밍은 별도의 모듈 스코프를 허용하여, 이름이 모듈 내에서 보이지만(모듈에 프라이빗) 외부에서는 보이지 않도록 한다. 함수 내에서 C와 같은 일부 언어는 블록 스코프를 허용하여 스코프를 함수의 하위 집합으로 제한하며, 함수형 언어는 표현식 스코프를 허용하여 스코프를 단일 표현식으로 제한한다. 다른 스코프에는 모듈 스코프와 유사하게 동작하는 파일 스코프(특히 C에서)와 함수 외부의 블록 스코프(특히 Perl에서)가 있다.
스코프가 정확히 언제 시작하고 끝나는가는 미묘한 문제이다. C와 같은 일부 언어에서는 이름의 스코프가 이름 선언에서 시작하므로 주어진 블록 내에서 선언된 다른 이름은 서로 다른 스코프를 가질 수 있다. 이는 함수를 사용하기 전에 선언해야 하지만 반드시 정의할 필요는 없으며, 상호 재귀의 경우 전방 선언이 필요하다. Python과 같은 다른 언어에서는 이름의 스코프가 이름이 선언된 관련 블록(예: 함수의 시작)의 시작 부분에서 시작하며, 정의된 위치와 관계없이 주어진 블록 내의 모든 이름이 동일한 스코프를 갖는다. JavaScript에서 `let` 또는 `const`로 선언된 이름의 스코프는 이름 선언에서 시작하고, `var`로 선언된 이름의 스코프는 이름이 선언된 함수의 시작 부분에서 시작하며, 이를 ''변수 호이스팅''이라고 한다. 정의되지 않은 값을 가진 컨텍스트에서 이름의 동작이 다르다. Python에서는 정의되지 않은 이름을 사용하면 런타임 오류가 발생하지만, JavaScript에서는 `var`로 선언된 정의되지 않은 이름이 함수 전체에서 사용 가능하며, 이는 암시적으로 값 `undefined`에 바인딩되기 때문이다.
스코프의 종류에는 표현식 영역, 블록 영역, 함수 영역, 파일 영역, 모듈 영역, 전역 영역 등이 있다.
2. 1. 전역 영역 (Global Scope)
전역 변수는 프로그램 전체에서 접근 가능한 변수이다. 많은 언어에서 전역 변수의 남용은 권장되지 않지만, 함수명이나 클래스명은 대부분 전역에서 접근 가능하다. 네임스페이스를 사용하면 전역 영역에서 변수나 함수명이 충돌하는 것을 막을 수 있다.[4]전역 범위를 가진 변수 이름은 '''전역 변수'''라고 불리며, 이름 충돌과 의도하지 않은 가림 현상, 그리고 낮은 모듈성 때문에 일부 언어에서는 좋지 않은 관행으로 여겨진다. 대신 함수 범위 또는 블록 범위가 더 선호된다. 그러나 전역 범위는 일반적으로 (언어에 따라) 함수 이름, 클래스 이름 및 기타 자료형 이름과 같은 다양한 다른 종류의 이름에 사용된다. 이러한 경우 네임스페이스와 같은 메커니즘을 사용하여 충돌을 피한다.[4]
이름 충돌을 피하기 위해 많은 언어는 전역 이름을 구성하는 메커니즘을 제공한다. C++C#의 ''네임스페이스'', 에이다의 ''패키지'', 표준 ML의 ''구조체'' 등이 그 예시이다. 객체 지향 언어에서는 클래스나 싱글톤 객체가 이러한 목적을 수행하기도 한다. 펄의 패키지는 C++의 네임스페이스와 유사하지만, 객체 지향 프로그래밍을 위한 클래스로 선택적으로 사용된다. 자바는 변수와 함수를 클래스로 구성하고, 해당 클래스를 에이다와 유사한 패키지로 구성한다.[4]
한국에서는 전역 변수 사용을 최소화하고, 모듈화 및 캡슐화를 통해 코드의 재사용성과 유지보수성을 높이는 방향으로 개발하는 것이 일반적이다.
2. 2. 모듈 영역 (Module Scope)
모듈라나 파이썬처럼 모듈을 지원하는 프로그래밍 언어에서는 모듈 단위 변수를 선언할 수 있다. 이러한 변수들은 같은 모듈 안에서만 접근할 수 있으며, 모듈 밖에서는 볼 수 없다.[4]C++(20 이전) 등 모듈 개념을 직접 지원하지 않는 일부 객체 지향 언어에서는 클래스가 모듈 영역의 역할을 대신하기도 한다. 모듈과 객체의 개념을 모두 지원하는 파이썬은 모듈 단위 영역과 클래스 단위 영역을 모두 지원한다.[4]
이름 바인딩의 범위는 모듈이며, 이를 '''모듈 범위'''라고 한다. 모듈 범위는 모듈(여러 파일에 걸쳐 있을 수 있음)이 복잡한 프로그램의 기본 단위인 모듈 프로그래밍 언어에서 사용할 수 있으며, 정보 은닉과 제한된 인터페이스 노출을 허용한다. 모듈 범위는 Modula 계열 언어에서 개척되었으며, Modula의 영향을 받은 파이썬이 대표적인 현대적 예이다.[4]
C++20 이전의 C++처럼 모듈을 직접 지원하지 않는 일부 객체 지향 프로그래밍 언어에서는 유사한 구조가 클래스 계층 구조에 의해 제공되며, 여기서 클래스가 프로그램의 기본 단위이고 클래스는 개인 메서드를 가질 수 있다. 이는 이름 해석 및 범위보다는 동적 디스패치의 맥락에서 제대로 이해되지만, 종종 유사한 역할을 한다. 파이썬처럼 모듈과 클래스 둘 다 가지고 있는 경우도 있으며, 코드 구성(모듈 수준 함수 또는 관례적으로 개인 메서드)은 프로그래머의 선택이다.[4]
2. 3. 파일 영역 (File Scope)
C와 C++ 등의 프로그래밍 언어에서는 소스 파일 최상위 수준에서 선언된 변수나 함수가 해당 파일 전체에서 접근 가능하고, 다른 파일에서는 접근할 수 없는 특성을 가진다. 이러한 파일 영역은 보다 원시적인 형태의 모듈 영역으로 간주된다.[18]파일 영역은 C (및 C++)에 특유한 것으로, 파일의 최상위 수준(어떤 함수 내에도 없는)에서 선언된 변수와 함수의 유효 범위는 전체 파일, 즉 C의 경우 선언에서 소스 파일의 끝까지, 더 정확히는 번역 단위(내부 연결)까지이다.[18]
C 언어에서 파일 영역은 변수와 함수의 가시성(visibility)을 제어하는 데 사용된다. 파일 영역에 선언된 변수나 함수는 해당 파일 내에서는 어디에서든 접근할 수 있지만, 다른 파일에서는 접근할 수 없다. 이는 내부 결합이라고도 불린다.[18]
```c
//file1.c
int global_var = 10; // 파일 영역 변수
void foo() { // 파일 영역 함수
// ...
}
```
위의 예시에서 `global_var` 변수와 `foo` 함수는 `file1.c` 파일 내에서는 어디에서든 접근할 수 있지만, 다른 파일에서는 접근할 수 없다.
C 프로그램은 여러 개의 소스 파일로 구성될 수 있으며, 각 소스 파일은 별도의 오브젝트 파일로 컴파일된다. 이후 링커를 통해 이러한 오브젝트 파일들이 하나의 실행 파일이나 라이브러리로 결합된다. 따라서, 파일 영역은 컴파일 단계에서는 번역 단위 내에서, 링크 단계에서는 번역 단위 간의 이름 확인(name resolution)에 영향을 미친다.[18]
파일 영역은 include 문을 통해서 확장이 가능하다. include 문은 변수와 함수를 내부 컨텍스트에 추가하고, 자체적으로 추가 include 문을 호출할 수 있기 때문에 파일 본문에 무엇이 컨텍스트에 있는지 결정하기 어려울 수 있다.[18]
C에서 함수 이름은 전역 유효 범위(C에서 extern 연결)를 갖는다. 함수 시그니처에 static을 추가하면 파일 유효 범위(내부 연결)가 된다.[18]
2. 4. 함수 영역 (Function Scope)
대부분의 프로그래밍 언어는 함수 내에서만 유효한 지역 변수를 제공한다. 함수 영역을 사용하는 지역 변수는 함수가 반환되면 더 이상 사용할 수 없다. 변수 `n`은 함수의 인자로, 함수 밖에서는 사용할 수 없다. 변수 `total`은 인자는 아니지만 함수 내에서 선언된 지역 변수로, 마찬가지로 함수 밖에서는 사용할 수 없다.[6]정적 영역 규칙에서는 함수가 다른 함수를 호출할 경우 변수는 영역 밖으로 벗어나게 되며, 해당 변수를 사용할 수 없게 된다. 그러나 동적 영역 규칙에서는 다른 함수가 호출되더라도 변수가 여전히 사용 가능하며, 함수가 최종적으로 종료될 때 비로소 변수가 영역 밖으로 벗어나게 된다. 동적 영역 규칙은 이러한 특성 때문에 소스 코드 단위에서 변수의 사용 가능 여부를 분석할 수 없고, 실행 시의 동작에 따라 변수의 사용 가능 여부가 바뀌게 된다. 이는 종종 찾아내기 힘든 버그를 만들어낼 수 있기 때문에, 현대 프로그래밍 언어들은 대부분 정적 영역 규칙을 사용한다.
정적 영역 규칙의 경우, 함수 `square`의 인자 `n`과 함수 `sum_of_squares`의 인자 `n`은 이름은 같지만 서로 별개의 영역에 선언되어 있다. 따라서 두 변수는 완전히 독립되어 서로 어떤 영향도 끼치지 않는다.
만약 동적 영역 규칙이 사용될 경우, `sum_of_squares`의 인자 `n`은 `square`의 인자 `n`과 겹치게 된다. 이 경우 이름 마스킹이 일어나, 기존의 `n` 변수를 가렸다가, 함수가 반환될 때 다시 원래 변수를 가리키게 될 것이다.
함수 내에서 선언된 변수의 범위가 해당 함수를 벗어나지 않을 때, 이를 '''함수 범위'''라고 한다. 함수 범위는 함수 또는 서브루틴 내에서 ''지역 변수''를 생성하는 방법을 제공하는 대부분의 프로그래밍 언어에서 사용 가능하다. 함수가 반환될 때 범위가 종료되는 변수이다. 대부분의 경우 변수의 수명은 함수 호출 기간이다. 즉, 함수가 시작될 때(또는 변수가 선언될 때) 생성되고 함수가 반환될 때 소멸되는 자동 변수이다. 변수의 범위는 함수 내에 있지만, "내부"의 의미는 범위가 어휘적인지 동적인지에 따라 다릅니다. 그러나 C와 같은 일부 언어는 정적 지역 변수도 제공하며, 변수의 수명은 프로그램의 전체 수명이지만, 변수는 함수 내에 있을 때만 유효하다. 정적 지역 변수의 경우 변수는 프로그램이 초기화될 때 생성되고 프로그램이 종료될 때만 소멸된다. 이는 정적 전역 변수와 같지만 자동 지역 변수처럼 함수 내에서만 유효하다.
예를 들어, `square`와 `sum_of_squares`라는 두 개의 함수가 정의되어 있다. `square`는 숫자의 제곱을 계산한다. `sum_of_squares`는 숫자까지의 모든 제곱의 합을 계산한다. (예를 들어, `square(4)`는 42 = `16`이고, `sum_of_squares(4)`는 02 + 12 + 22 + 32 + 42 = `30`이다.)
이러한 각 함수에는 함수의 인수를 나타내는 n이라는 변수가 있다. 이 두 n 변수는 동일한 이름을 가짐에도 불구하고 완전히 분리되어 있으며 관련이 없다. 각 변수의 범위가 자체적이고 분리된 함수이기 때문에 겹치지 않는다. 따라서 `sum_of_squares`는 자체 n이 변경되지 않고 `square`를 호출할 수 있다. 마찬가지로 `sum_of_squares`에는 total 및 i라는 변수가 있다. 이러한 변수는 제한된 범위로 인해 다른 함수에 속할 수 있는 total 또는 i라는 변수와 간섭하지 않는다. 즉, 이러한 이름과 관련 없는 이름, 심지어 동일한 이름이더라도 ''이름 충돌''의 위험이 없다.
이름 마스킹이 발생하지 않는다. 범위가 겹치지 않으므로 한 번에 n이라는 하나의 변수만 유효하다. 반대로, 동적 범위를 가진 언어로 비슷한 코드를 작성하면 호출하는 함수의 n이 호출된 함수에서 유효하게 된다. 즉, 범위가 겹치고 호출된 함수의 새로운 n에 의해 마스킹("가려짐")된다.
함수가 일급 함수이고 함수에 로컬로 생성된 다음 반환될 수 있는 경우 함수 범위는 상당히 더 복잡해진다. 이 경우 중첩 함수에서 로컬이 아닌 변수(함수 정의에서 바인딩되지 않고 둘러싸는 문맥의 변수로 확인되는 변수)는 클로저를 생성한다. 함수 자체뿐만 아니라 해당 문맥(변수)도 반환되어야 하고 잠재적으로 다른 문맥에서 호출되어야 하기 때문이다. 이는 컴파일러에서 훨씬 더 많은 지원이 필요하며 프로그램 분석을 복잡하게 만들 수 있다.
지역 변수, 즉 특정 함수 내에서만 존재하는 범위가 제한된 변수 이름을 사용하면 동일한 이름의 두 변수 간의 이름 충돌 위험을 피하는 데 도움이 된다. 그러나 이 질문에 대한 답에는 매우 다른 두 가지 접근 방식이 있다. 즉, 함수 "내"에 있다는 것은 무엇을 의미하는가?
'''어휘적 범위''' (또는 '''어휘적 스코핑'''; '''정적 범위''' 또는 '''정적 스코핑'''이라고도 함)에서 변수 이름의 범위가 특정 함수인 경우 해당 범위는 함수 정의의 프로그램 텍스트이다. 해당 텍스트 내에서 변수 이름이 존재하고 변수의 값에 바인딩되지만, 해당 텍스트 외부에서는 변수 이름이 존재하지 않는다. 반대로, '''동적 범위''' (또는 '''동적 스코핑''')에서 변수 이름의 범위가 특정 함수인 경우 해당 범위는 함수가 실행되는 기간이다. 함수가 실행되는 동안에는 변수 이름이 존재하고 해당 값에 바인딩되지만, 함수가 반환된 후에는 변수 이름이 존재하지 않는다. 즉, 함수
f
가 별도로 정의된 함수 g
를 호출하는 경우, 어휘적 범위에서는 함수 g
가 f
의 지역 변수에 접근할 수 ''없습니다'' (g
의 텍스트가 f
의 텍스트 내에 있지 않다고 가정한다). 반면, 동적 범위에서는 함수 g
가 f
의 지역 변수에 접근할 수 ''있습니다'' (g
가 f
의 호출 중에 호출되므로).'''어휘 범위'''를 사용하면 이름은 항상 어휘적 문맥을 참조한다. 이는 프로그램 텍스트의 속성이며 언어 구현에 의해 런타임 호출 스택과 독립적으로 만들어진다. 이 일치는 정적 프로그램 텍스트의 분석만 필요하기 때문에 이러한 유형의 범위는 '''정적 범위'''라고도 한다. 어휘 범위는 파스칼, Modula-2 및 Ada와 같은 모든 ALGOL 기반 언어뿐만 아니라 ML 및 Haskell과 같은 현대적 함수형 언어에서도 표준으로 사용된다. 또한 C 언어와 구문 및 의미론적 관련 언어에서도 다양한 종류의 제한 사항과 함께 사용된다. 정적 범위는 프로그래머가 매개변수, 변수, 상수, 타입, 함수 등과 같은 객체 참조를 단순한 이름 대체로 추론할 수 있도록 한다. 이렇게 하면 로컬 명명 구조를 개별적으로 이해할 수 있으므로 모듈식 코드를 작성하고 이에 대해 추론하는 것이 훨씬 쉬워진다. 반대로, 동적 범위는 프로그래머가 모듈의 코드가 호출될 수 있는 모든 가능한 실행 문맥을 예측하도록 강요한다.
일급 함수 중첩 함수가 있는 언어에서 어휘 범위를 올바르게 구현하는 것은 각 함수 값이 의존하는 변수 값의 기록을 함께 전달해야 하므로(함수와 이 문맥의 쌍을 클로저라고 함) 쉽지 않다. 구현 및 컴퓨터 아키텍처에 따라 매우 깊이 어휘적으로 중첩된 함수를 사용할 때 변수 조회가 약간 비효율적일 ''수도'' 있지만 이를 완화하는 잘 알려진 기술이 있다. 또한 자체 인자 및 (즉시) 로컬 변수만 참조하는 중첩 함수의 경우 모든 상대적 위치를 컴파일 시간에 알 수 있다. 따라서 해당 유형의 중첩 함수를 사용할 때는 오버헤드가 전혀 발생하지 않는다. 중첩 함수를 사용하지 않는 프로그램의 특정 부분과 중첩 함수를 사용할 수 없는 언어(예: C 언어)로 작성된 프로그램에도 동일하게 적용된다.
2. 5. 블록 영역 (Block Scope)
알골과 그 후손인 C, 그리고 그에 영향을 받은 많은 현대 언어들은 블록 단위 지역 변수를 지원한다.[5]예를 들어, 다음 C 코드에서 변수 `i`는 `for` 문 안에서만 유효한 루프 변수이다.
```c
unsigned int sum_of_squares(unsigned int n) {
unsigned int ret = 0;
for(unsigned int i = 0; i <= n; ++i) {
unsigned int n_squared = i * i;
ret += n_squared;
}
return ret;
}
```
또한 `n_squared`는 블록 안에서 선언된 변수로, 블록 안에서만 유효하다. 한편 이 블록은 함수 안에 들어 있으므로, 블록 바깥에서 선언된 변수인 `ret`는 블록 안에서도 사용 가능하다. 블록 영역은 여러 개의 블록 안에서 선언된 변수들이 서로 간섭하지 않도록 하여, 에러의 가능성을 줄여 준다.
블록 영역 변수는 위 코드에서처럼 루프나 조건문에서 유용하게 사용할 수 있지만, 변수 영역을 제한하기 위한 용도로 블록을 따로 선언하는 것도 가능하다.
블록은 주로 if, while, for 루프와 같은 제어 흐름에 사용되며, 이러한 경우 블록 영역은 변수의 범위가 함수의 실행 흐름 구조에 따라 달라짐을 의미한다. 그러나 블록 영역을 가진 언어는 일반적으로 "네이키드" 블록의 사용도 허용하며, 이는 변수 범위의 세분화된 제어를 허용하는 유일한 목적을 가진다.
Algol 68 및 C와 같은 여러 프로그래밍 언어의 미묘한 점은 (위 예에서 보여지고 C99부터 표준화됨) 블록 영역 변수는 블록 본문 내뿐만 아니라 (있는 경우) 제어 문 내에서도 선언될 수 있다는 것이다. 이는 함수 선언에서 (함수 본문 블록이 시작되기 전에) 선언되고 전체 함수 본문에 대한 범위 내에 있는 함수 매개변수와 유사하다. 이는 주로 for 루프에서 사용되며, while 루프와 달리 루프 조건과 별도로 초기화 문이 있으며 일반적인 관용구이다.
블록 영역은 섀도잉에 사용될 수 있다. 블록 내에서 보조 변수는 매개변수 이름을 가릴 수 있지만, 이는 오류의 가능성 때문에 좋지 않은 스타일로 간주된다. 또한, Java와 C#과 같이 블록 영역을 지원하는 C의 일부 후손 언어는 한 지역 변수가 다른 변수를 숨기는 것을 허용하지 않는다.
블록이 변수의 값을 설정하는 데 사용되는 경우, 블록 영역은 블록 외부에서 변수를 선언해야 한다. 이것은 단일 할당을 가진 조건문의 사용을 복잡하게 만든다.
일부 언어는 함수 외부에서 블록 영역 개념을 다양한 정도로 적용할 수 있다.
2. 6. 표현식 영역 (Expression Scope)
대부분의 함수형 프로그래밍 언어는 `let` 문을 지원한다. 예를 들어 ML에서 `let val x = f() in x * x end`와 같은 표현식에서, 변수 x는 이 표현식 안에서만 유효하다. 이와 같은 표현식은 `f()`를 두 번 호출하는 낭비를 막기 위해 쓰일 수 있다.표현식 범위는 이름 바인딩의 표현식으로, '''표현식 영역'''이라고 불린다. 표현식 영역은 많은 언어, 특히 선언의 범위를 단일 표현식으로 허용하는 ''let 표현식''이라는 기능을 제공하는 함수형 언어에서 사용할 수 있다. 이는 예를 들어 계산에 중간값이 필요한 경우 편리하다. 예를 들어, 표준 ML에서 `f()`가 `12`를 반환하면 `let val x = f() in x * x end`는 `144`로 평가되는 표현식이며, `f()`를 두 번 호출하는 것을 피하기 위해 `x`라는 임시 변수를 사용한다. 블록 범위를 가진 일부 언어는 표현식에 블록을 포함하는 구문을 제공하여 이 기능을 근사한다. 예를 들어, 앞서 언급한 표준 ML 표현식은 Perl에서 `do { my $x = f(); $x * $x }`로, 또는 GNU C에서 `({ int x = f(); x * x; })`로 작성할 수 있다.
파이썬에서 제너레이터 표현식 및 리스트 컴프리헨션(파이썬 3)의 보조 변수는 표현식 범위를 갖는다.
C에서 함수 프로토타입의 변수 이름은 '''함수 프로토콜 범위'''로 알려진 표현식 범위를 갖는다. 프로토타입의 변수 이름은 참조되지 않으므로(실제 정의와 다를 수 있음)—단순히 더미이므로— 종종 생략되지만, 예를 들어 문서를 생성하는 데 사용될 수 있다.
3. 정적 영역 규칙 (Lexical Scope)
알골과 C를 비롯한 대부분의 현대 프로그래밍 언어에서 사용되는 정적 영역 규칙은 변수의 유효 영역이 소스 코드 상의 구조를 바탕으로 결정되는 방식이다. 이 규칙에 따르면 변수 영역은 실행 시간의 호출 스택이나 언어 구현에 영향을 받지 않고, 소스 코드만으로 파악할 수 있다. 그래서 '문법적 영역 규칙'(lexical scoping rule)이라고도 불린다.[1]
정적 영역 규칙은 구조적 프로그래밍에서 변수의 수명 주기를 쉽게 파악하여 프로그램의 모듈성을 높이는 데 기여한다. 함수형 프로그래밍 언어에서는 함수 선언 시의 변수 환경을 함께 갖는 클로저라는 자료구조가 중요한 역할을 한다.[10]
이름(식별자)의 어휘적 "범위"에 대한 엄격한 정의는 명확하다. 어휘적 범위는 "이름과 개체의 바인딩이 적용되는 소스 코드의 일부"이다. 이는 1960년 ALGOL 60의 명세에서 정의된 이후 거의 변경되지 않았다.
C (2007)에서는 다음과 같이 정의한다.[2]
: 식별자는 객체, 함수, 구조체, 공용체 또는 열거형의 태그 또는 멤버, typedef 이름, 레이블 이름, 매크로 이름 또는 매크로 매개변수를 나타낼 수 있다. 동일한 식별자가 프로그램의 다른 지점에서 다른 개체를 나타낼 수 있다. [...] 식별자가 지정하는 각 다른 개체에 대해 식별자는 해당 ''범위''라고 하는 프로그램 텍스트 영역 내에서만 ''보이는''(즉, 사용할 수 있는) 상태가 된다.
Go (2013)에서는 다음과 같이 정의한다.[3]
: 선언은 빈칸이 아닌 식별자를 상수, 타입, 변수, 함수, 레이블 또는 패키지에 바인딩한다. [...] 선언된 식별자의 범위는 해당 식별자가 지정된 상수, 타입, 변수, 함수, 레이블 또는 패키지를 나타내는 소스 텍스트의 범위이다.
대부분의 현대 언어는 변수와 함수에 렉시컬 스코프를 사용하지만, 동적 스코프는 일부 언어, 특히 일부 Lisp 방언, 일부 "스크립팅" 언어, 일부 템플릿 언어에서 사용된다. 펄 5는 렉시컬 스코프와 동적 스코프를 모두 제공한다. 렉시컬 스코프 언어에서도, 클로저의 스코프는 초보자에게 혼란스러울 수 있는데, 이는 클로저가 호출되는 위치가 아니라 정의되는 렉시컬 컨텍스트에 따라 달라지기 때문이다.
렉시컬 확인은 컴파일 시간에 결정될 수 있으며, ''초기 바인딩''이라고도 하며, 동적 확인은 일반적으로 런타임에만 결정될 수 있으며, 따라서 ''후기 바인딩''이라고 한다.
4. 동적 영역 규칙 (Dynamic Scope)
동적 영역 규칙을 사용하는 언어는 보통 호출 스택에 변수의 바인딩을 포함한다. 변수가 선언될 때 스택의 맨 위에 각 변수가 선언되며, 프로그램의 흐름이 변수 영역을 벗어날 때 스택의 맨 위가 팝(pop)된다.[10] 호출 스택은 런타임에만 동작하므로, 컴파일 타임에는 동적 영역 규칙을 실행할 수 없다.
동적 영역 규칙을 사용하는 언어로는 펄, 커먼 리스프 등이 있다.[10] C 언어 전처리기를 비롯한 대부분의 언어에서 매크로의 변수 영역은 동적 영역 규칙과 유사하다.
동적 영역에서 이름 확인은 이름을 만났을 때의 프로그램 상태에 따라 달라지며, ''실행 컨텍스트''(혹은 ''런타임 컨텍스트'', ''호출 컨텍스트'', ''동적 컨텍스트'')에 의해 결정된다.[10] 동적 영역에서 이름은 로컬 실행 컨텍스트를 검색하여 확인되며, 실패하면 외부 실행 컨텍스트를 검색하는 식으로 호출 스택을 따라 진행된다.[10]
동적 영역 규칙은 현대 언어에서는 흔하지 않다.[10] Perl 및 Common Lisp와 같은 일부 언어에서는 변수를 정의하거나 재정의할 때 정적/동적 영역 규칙을 선택할 수 있다. 동적 영역 규칙을 사용하는 언어의 예로는 Logo, Emacs Lisp, LaTeX 및 셸 언어 bash, dash, PowerShell이 있다.
동적 영역 규칙은 코드 분석 및 예측을 어렵게 만들 수 있으므로, 주의해서 사용해야 한다.
5. 객체 지향 프로그래밍과 변수 영역
객체 지향 프로그래밍에서는 클래스, 인스턴스, 메서드 등의 개념과 함께 변수 영역이 더욱 다양해진다.
Modula 계열 언어는 모듈 프로그래밍을 통해 정보 은닉과 제한된 인터페이스 노출을 허용하는 모듈 범위를 사용한다. Modula의 영향을 받은 파이썬이 대표적인 현대적 예시이다. C++처럼 모듈을 직접 지원하지 않는 일부 객체 지향 프로그래밍 언어에서는 클래스 계층 구조를 통해 유사한 기능을 제공한다.
자바는 어휘적 유효 범위를 가지며, 여러 종류의 변수를 갖는다.[19]
- 지역 변수: 메서드 내부 또는 특정 블록 내에서 정의되며, 정의된 위치와 하위 레벨에 국한된다.
- 멤버 변수: '필드'라고도 하며, 클래스 내에서, 메서드 외부에서 선언된다. 기본적으로 클래스 내의 모든 메서드와 패키지의 모든 클래스에서 사용할 수 있다.
- 매개변수: 메서드 선언의 변수이다.
클래스 내 최상위 레벨의 변수는 정의에 사용된 수정자에 따라 접근 범위가 달라진다.[20]
수정자 | 클래스 | 패키지 | 서브클래스 | 월드 |
---|---|---|---|---|
public | 예 | |||
protected | 아니오 | |||
(수정자 없음) | 아니오 | |||
private | 아니오 |
인스턴스 스코프와 클래스 스코프 외에도, 전역 스코프, 파일 스코프, 지역 스코프 등이 존재한다.
5. 1. 인스턴스 영역 (Instance Scope)
객체 지향 프로그래밍에서, 클래스 기반 객체 지향 언어의 경우 각 인스턴스에 할당된 변수(필드)나 함수(메서드)는 해당 인스턴스를 통해서만 참조될 수 있다. 인스턴스 변수는 멤버 변수라고도 하며, 함수는 멤버 함수라고도 한다. `this`나 `self`와 같은 객체 참조를 통해 스코프를 명시한다.5. 2. 클래스 영역 (Class Scope)
객체 지향 언어에서 클래스 영역은 특정 클래스 정의 전체에서 참조 가능한 클래스 변수나 클래스 메서드를 정의할 수 있는 영역이다. 클래스 외부에서는 클래스 이름으로 수식하여 스코프를 명시한다. 이는 일종의 제한된 전역 스코프로 생각할 수 있다.[36]클래스 영역에 속하는 변수는 클래스 변수, 함수는 클래스 메서드라고 불린다. 클래스 전체에서 공유되므로, 제한된 전역 스코프와 유사하게 작동한다. 클래스 외부에서 이들에 접근할 때는 클래스 이름을 사용하여 스코프를 명확히 해야 한다.[36]
한국에서는 객체 지향 프로그래밍이 널리 사용되므로, 인스턴스 영역과 클래스 영역에 대한 명확한 이해가 필수적이다. 특히, Java, C++, C# 등의 언어에서는 이러한 개념이 더욱 중요하다.
클래스 스코프가 없는 언어의 경우, 파일 스코프를 사용하여 비슷한 기능을 구현할 수 있다.
참조
[1]
문서
'Report on the Algorithmic Language Algol 60'
[2]
웹사이트
WG14 N1256
http://www.open-std.[...]
2007-09-07
[3]
웹사이트
The Go Programming Language Specification
http://golang.org/re[...]
[4]
웹사이트
Code Conventions for the JavaScript Programming Language
https://docs.angular[...]
2015-01-04
[5]
간행물
Report on the algorithmic language ALGOL 60
[6]
웹사이트
Functions - Javascript:MDN
https://developer.mo[...]
2023-04-23
[7]
웹사이트
N4720: Working Draft, Extensions to C++ for Modules
https://isocpp.org/f[...]
2019-04-30
[8]
문서
Programming Language Pragmatics
http://booksite.else[...]
[9]
문서
A Symbol Table Abstraction to Implement Languages with Explicit Scope Control
http://origin-www.co[...]
LeBlank-Cook
1983
[10]
웹사이트
CSE 341 -- Lexical and Dynamic Scoping
https://web.archive.[...]
University of Washington
[11]
서적
Proceedings of the 1982 ACM symposium on LISP and functional programming - LFP '82
1982-08
[12]
간행물
The Function of FUNCTION in LISP
MIT Artificial Intelligence Lab
1970-06
[13]
간행물
The Art of the Interpreter; or, The Modularity Complex (Parts Zero, One and Two).
MIT Artificial Intelligence Lab
1978-05
[14]
웹사이트
History of T
http://www.paulgraha[...]
2020-02-05
[15]
간행물
RABBIT: A Compiler for SCHEME
Massachusetts Institute of Technology
1978-05
[16]
서적
lexical scope
https://books.google[...]
University of Michigan. Engineering Summer Conferences
1967
[17]
서적
lexical scoping
https://books.google[...]
1970
[18]
웹사이트
Scope
http://publib.boulde[...]
IBM
[19]
웹사이트
Declaring Member Variables (The Java Tutorials > Learning the Java Language > Classes and Objects)
https://docs.oracle.[...]
2018-03-19
[20]
웹사이트
Controlling Access to Members of a Class (The Java Tutorials > Learning the Java Language > Classes and Objects)
https://docs.oracle.[...]
2018-03-19
[21]
웹사이트
Everything you need to know about Javascript variable scope
http://www.coolcoder[...]
Saurab Parakh
2010-02-08
[22]
웹사이트
Annotated ES5
https://es5.github.i[...]
2018-03-19
[23]
웹사이트
Functions
https://developer.mo[...]
2018-03-19
[24]
웹사이트
12.2 Variable Statement
https://es5.github.i[...]
2012-05-28
[25]
웹사이트
JavaScript Scoping and Hoisting
http://www.adequatel[...]
Ben Cherry
2010-02-08
[26]
웹사이트
Javascript Closures
http://jibbering.com[...]
Richard Cornford
2004-03
[27]
웹사이트
Explaining JavaScript Scope And Closures
http://robertnyman.c[...]
Robert Nyman
2008-10-09
[28]
웹사이트
The Revised Maclisp Manual (The Pitmanual), Sunday Morning Edition
http://www.maclisp.i[...]
HyperMeta Inc.
2007-12-16
[29]
웹사이트
The Revised Maclisp Manual (The Pitmanual), Sunday Morning Edition
http://www.maclisp.i[...]
HyperMeta Inc.
2007-12-16
[30]
웹사이트
Common Lisp HyperSpec
http://www.lispworks[...]
LispWorks Ltd.
1996
[31]
웹사이트
Programming Language ISLISP, ISLISP Working Draft 23.0
http://www.islisp.in[...]
2018-10-20
[32]
웹사이트
Lexical Binding
https://www.emacswik[...]
2018-10-20
[33]
웹사이트
R FAQ
https://cran.r-proje[...]
2018-03-19
[34]
웹사이트
DCL19-C. 変数と関数の有効範囲を最小限にする
https://www.jpcert.o[...]
[35]
문서
記憶域期間 (storage duration) あるいはエクステント (extent) とも。
[36]
문서
Microsoft Visual C++에서는, 독자 확장으로서 `__super` 키워드에 의한 에일리어스를 서포트하고 있다.
[37]
문서
lexical
본 사이트는 AI가 위키백과와 뉴스 기사,정부 간행물,학술 논문등을 바탕으로 정보를 가공하여 제공하는 백과사전형 서비스입니다.
모든 문서는 AI에 의해 자동 생성되며, CC BY-SA 4.0 라이선스에 따라 이용할 수 있습니다.
하지만, 위키백과나 뉴스 기사 자체에 오류, 부정확한 정보, 또는 가짜 뉴스가 포함될 수 있으며, AI는 이러한 내용을 완벽하게 걸러내지 못할 수 있습니다.
따라서 제공되는 정보에 일부 오류나 편향이 있을 수 있으므로, 중요한 정보는 반드시 다른 출처를 통해 교차 검증하시기 바랍니다.
문의하기 : help@durumis.com