실제로 그렇게하기가 그렇게 어렵지 않기 때문에 이러한 일을 할 수 있습니다.
컴파일러의 관점에서 다른 함수 안에 함수 선언을 갖는 것은 구현하기가 매우 간단합니다. 컴파일러는 int x;
어쨌든 함수 내부의 다른 선언 (예 :)을 처리하기 위해 함수 내부의 선언을 허용하는 메커니즘이 필요 합니다.
일반적으로 선언을 구문 분석하기위한 일반적인 메커니즘이 있습니다. 컴파일러를 작성하는 사람에게는 다른 함수의 내부 또는 외부에서 코드를 구문 분석 할 때 해당 메커니즘이 호출되는지 여부는 전혀 문제가되지 않습니다. 이는 단지 선언 일 뿐이므로 선언이 무엇인지 충분히 알 수있을 때 선언을 처리하는 컴파일러의 일부를 호출합니다.
사실, 함수 내에서 이러한 특정 선언을 금지하는 것은 아마도 추가 복잡성을 추가 할 것입니다. 왜냐하면 컴파일러는 함수 정의 내에서 이미 코드를보고 있는지 확인하기 위해 완전히 무상 검사를 필요로하고이를 기반으로이 특정을 허용할지 금지할지 여부를 결정하기 때문입니다. 선언.
그것은 중첩 함수가 어떻게 다른지에 대한 질문을 남깁니다. 중첩 된 함수는 코드 생성에 미치는 영향으로 인해 다릅니다. 중첩 함수를 허용하는 언어 (예 : Pascal)에서는 일반적으로 중첩 함수의 코드가 중첩 된 함수의 변수에 직접 액세스 할 수 있다고 예상합니다. 예를 들면 :
int foo() {
int x;
int bar() {
x = 1;
}
}
지역 함수가 없으면 지역 변수에 액세스하는 코드는 매우 간단합니다. 일반적인 구현에서 실행이 함수에 들어가면 로컬 변수를위한 일부 공간 블록이 스택에 할당됩니다. 모든 지역 변수는 해당 단일 블록에 할당되며 각 변수는 블록의 시작 (또는 끝)에서 단순히 오프셋으로 처리됩니다. 예를 들어 다음과 같은 함수를 생각해 봅시다.
int f() {
int x;
int y;
x = 1;
y = x;
return y;
}
컴파일러 (추가 코드를 최적화하지 않았다고 가정)는 대략 다음과 같은 코드를 생성 할 수 있습니다.
stack_pointer -= 2 * sizeof(int);
x_offset = 0;
y_offset = sizeof(int);
stack_pointer[x_offset] = 1;
stack_pointer[y_offset] = stack_pointer[x_offset];
return_location = stack_pointer[y_offset];
stack_pointer += 2 * sizeof(int);
특히, 지역 변수 블록의 시작을 가리키는 하나의 위치 가 있으며 지역 변수에 대한 모든 액세스는 해당 위치에서 오프셋으로 이루어집니다.
중첩 된 함수를 사용하면 더 이상 그렇지 않습니다. 대신 함수는 자체 지역 변수뿐만 아니라 중첩 된 모든 함수의 지역 변수에 액세스 할 수 있습니다. 오프셋을 계산하는 하나의 "stack_pointer"를 갖는 대신 스택을 백업하여 중첩 된 함수에 대한 로컬 stack_pointer를 찾아야합니다.
이제, 모두가 아니다 사소한 경우에 그 끔찍한 중 - 경우 bar
의 중첩 된 내부는 foo
다음 bar
바로 액세스 이전의 스택 포인터의 스택을 볼 수 foo
의 변수를. 권리?
잘못된! 이것이 사실 일 수있는 경우가 있지만 반드시 그런 것은 아닙니다. 특히,bar
재귀적일 수 있습니다.이 경우 주어진 호출은bar
주변 함수의 변수를 찾기 위해 스택을 백업하는 거의 임의의 수의 수준을 찾아야 할 수도 있습니다. 일반적으로 두 가지 중 하나를 수행해야합니다. 스택에 추가 데이터를 넣어 런타임에 스택을 검색하여 주변 함수의 스택 프레임을 찾을 수 있도록하거나, 아니면 포인터를 효과적으로 전달합니다. 중첩 된 함수에 대한 숨겨진 매개 변수로 주변 함수의 스택 프레임. 오,하지만 주변 함수가 반드시 하나만있는 것은 아닙니다. 함수를 중첩 할 수 있다면 아마도 임의의 깊이 중첩 (더 많거나 적음) 할 수 있으므로 임의의 수의 숨겨진 매개 변수를 전달할 준비가되어 있어야합니다. 즉, 일반적으로 스택 프레임을 주변 기능에 연결 한 목록과 같은 결과를 얻게됩니다.
그러나 이는 "로컬"변수에 대한 액세스가 사소한 문제가 아닐 수도 있음을 의미합니다. 변수에 액세스하기 위해 올바른 스택 프레임을 찾는 것은 사소한 일이 아니므로 주변 함수의 변수에 대한 액세스도 실제 로컬 변수에 액세스하는 것보다 (적어도 일반적으로) 느립니다. 물론 컴파일러는 올바른 스택 프레임을 찾고 임의의 수의 스택 프레임을 통해 변수에 액세스하기 위해 코드를 생성해야합니다.
이 C가 중첩 함수를 금지함으로써 피했던 복잡성입니다. 현재의 C ++ 컴파일러가 1970 년대의 빈티지 C 컴파일러와는 다소 다른 종류의 짐승이라는 것은 확실히 사실입니다. 다중 가상 상속과 같은 경우 C ++ 컴파일러는 어떤 경우에도 이와 동일한 일반적인 특성을 처리해야합니다 (즉, 이러한 경우 기본 클래스 변수의 위치를 찾는 것도 중요하지 않을 수 있음). 백분율 기준으로 중첩 된 함수를 지원한다고해서 현재 C ++ 컴파일러에 많은 복잡성이 추가되지는 않습니다 (그리고 gcc와 같은 일부는 이미 지원함).
동시에, 그것은 거의 유용성을 추가하지 않습니다. 특히 함수 내부에서 함수처럼 작동 하는 것을 정의하려는 경우 람다 식을 사용할 수 있습니다. 이것이 실제로 생성하는 것은 함수 호출 연산자 ( operator()
) 를 오버로드하는 객체 (즉, 일부 클래스의 인스턴스 )이지만 여전히 함수와 유사한 기능을 제공합니다. 하지만 주변 컨텍스트에서 데이터를 캡처 (또는 사용하지 않음)하면 완전히 새로운 메커니즘과 사용 규칙 세트를 개발하는 대신 기존 메커니즘을 사용할 수 있습니다.
결론 : 처음에는 중첩 된 선언이 어렵고 중첩 된 함수가 사소한 것처럼 보일 수도 있지만, 그 반대는 사실입니다. 중첩 된 함수는 실제로 중첩 된 선언보다 지원하기가 훨씬 더 복잡합니다.
one
는 함수 정의 이고 다른 두 가지는 선언 입니다.