0. 개요
🤔 궁금증 or 들어가기 전
우리가 프로그래밍 하면서 아주 자연스럽게 사용했던 중첩된 서브루틴에 대해 알아보자.
중첩 subroutine 내에서 선언된 것들과 스코프 등에 대한 이야기를 해보자.
(서브루틴 = 함수 or 메서드 정도로 생각하면 편하다)
1. Nested Subrotuines (중첩 서브루틴)
- 많은 언어에서 중첩 서브루틴을 지원한다.
ex. Algol 60, Ada, ML, Commn Lisp, Python, Scheme, Swift 등
오히려 C언어 계열에서는 불가능
1-1. Algol 계열의 Nested Subroutines
- 중첩 서브루틴 내에서 선언된 상수, 타입, 변수 또는 서브루틴은 해당 스코드 외부에서는 보이지 않는다. (Not-visible)
- 이름과 객체 간의 바인딩에 대한 'the closest nested scope rule'을 만든다.
1-2. The Closet Nested Scope rule
- 선언에서 도입된 이름은 선언된 스코프에서 알려지며,
다른 중첩된 스코프에서 숨겨지지 않는 한 각 내부 중첩된 스코프에서 알려진다.
{ a
//scope1
{ }
//scope2
{ }
//scope3
{ } // a 가 중첩된 어디에도 없다는 가정하에
}
위와 같이 a 는 모든 스코프 1, 2, 3에 알려진다.
(단, 같은 이름인 a 이 중첩 스코프에서 사용되어 숨겨지는 경우 바깥쪽 a가 숨겨지게 되는 경우 제외)
- 현재 가장 안쪽 스코프에서 해당 이름과 연결된 선언이 있는 경우! 해당 이름에 대한 활성 바인딩을 정의한다.
그렇지 않으면 주어진 이름을 현재 가장 안쪽 스코프에서 둘러싸고 있는 스코프 및 바깥쪽으로 더 넓게 검색한다. - 즉, 해당 스코프에서 선언이 있다면, 그 이름과 바인딩
- 선언이 없다면? Find -> 해당 스코프부터 프로그램의 가장 바깥 중첩 수준까지 이동하여 전역 객체가 선언된 곳까지 검색
1-3. Built-in, Predefined Object
해석해보면, 내장 함수 혹은 이미 정의된 객체.
이것들을 가장 바깥의 scope, 보이지 않는 추가적인 외부 scope로 볼 수 있다.
ex. I/O 루틴, math 함수 등
2. Scope 내의 Hole
2-1. Hidden, Hole
- 내부 서브루틴에서 선언한 name과 같은 이름의 name이 이미 외부에 선언되어 있다면,
외부 name은 해당 scope에서 숨겨지게 된다. (Hidden)
- Ada 혹은 파스칼 언어는 선언 후 실행이 이루어진다.
procedure 의 선언 후, 코드 가장 마지막 단에 begin과 end로 실제 실행이 이루어진다.
- P3는 A1이나 X와 같은 P1에서 선언된 변수에도 접근할 수 있다.
- P3는 P2 내부에서만 보이며 외부에서 접근할 수 없다.
- P1 과 F1 모두 "X"라는 이름의 로컬 변수를 선언!
- 중첩 서브루틴 내부의 변수 선언은 해당 스코프 내에서는 같은 이름의 외부 스코프 변수를 가리게 된다.
=> 변수 숨김으로 인한 "Hole" 발생 - 즉, F1 내부에서 "X"를 사용하면, 내부 "X"를 참조하게 되고,
다른 부분에서 "X"를 사용하면, 외부 "X"를 참조하게 된다. - -> 중첩 스코프에서 "비활성화(deactivate)"를 통해 외부 변수를 숨김으로써
변수 이름 충돌을 방지, 중첩 서브루틴이 자체적으로 독립적인 변수를 가질 수 있도록 해준다.
2-2. 언어 별 Hole 처리
동일한 이름의 중첩된 선언으로 가려진 바인딩을 처리하는 방식은 언어별로 다르다.
- 일반적으로는 hidden된 바인딩에 접근할 수 없도록 한다.
- 그러나, 몇몇 언어에서는 사용할 수 있는 방법을 제공해 준다.
1. Ada : '.' 사용 -> 함수명.변수명
2. C++ : '::' 사용 -> ::변수명
3. Nonlocal Objects에 접근하는 방법
3-1. Local Objects(지역 변수)에 접근하는 방법
compiler는 offeset을 사용하여 접근한다.
실행 중 subroutine의 프레임 포인터 레지스터를 가리키도록 한다.
3-2. Nonocal Objects(지역 변수)에 접근하는 방법
이론적으로는 알고 있다.
안쪽 (해당) 스코프에서 외부 스코프로 천천히 찾아나가면서 찾아보면 된다.
어떤 logic으로 해당 방법을 구현할 수 있을까?
- 가장 간단한 방법은 "static link"를 이용하는 방법
: 이 정적 링크는 lecically surrounding subroutine의 가장 최근 호출 프레임을 가르킨다.
= 즉, 부모 프레임을 가리키고 있다.