함수 호출 및 귀속

3109 단어 함수 호출귀속

인용문


"귀환은 귀환이 끝날 때까지 자신을 호출하는 것이다."이것은 대부분의 자료가 귀속에 대한 정의로 절차적으로도 일반적인 관감에 부합된다.그러나 함수의 호출 과정을 잘 모르면 이 말에 대한 이해가 모호해진다.함수는 어떻게 자신을 호출하고, 또 어떻게 되돌아옵니까?다음은 반복 호출을 설명하는 데 자주 사용되는 예입니다.
/******
*
* n n!
*
*/
int factorial( int n )
{
    if( n == 0 )
        return 1;
    else
        return n * factorial( n - 1 );
}

귀속은 프로그램 언어의 정수 개념 중 하나로 철저하게 파악하는 것은 분명히 쉽지 않을 것이다."세계는 복잡하기 때문에 세계의 이론을 해석하는 것은 간단하고 풍부한 변화를 가져야만 실행할 수 있다."

함수 호출


첫 번째 문장의 귀속 정의에는'호출'이라는 개념이 포함되어 있다.프로그램은main 함수로부터 한 걸음 한 걸음 호출 함수를 사용하여 프로그램 설계의 목적을 달성한다.분명히'함수 호출'도 프로그램 언어의 정수 개념 중의 하나이며 귀속 개념의 전도이다.
호출 과정은 컴퓨터 CPU 내부의 일부 개념과 관련된다. 가장 주요한 것은 창고(stack), 창고 프레임(fram stack), 반환값(일반적으로ax 레지스터를 통해), 주소 레지스터(bp), 창고 레지스터(sp), 명령 지침 레지스터(ip) 등이 있다.간단명료하게 말하자면 모든 함수는 운행 과정에서 창고에 임시 변수, 즉 중간 결과를 창고 프레임 형식으로 저장한다.만약 그것이 다른 함수를 호출한다면 전달해야 할 매개 변수도 창고 프레임을 통해 전달된다. (서로 다른 체계 구조는 다를 수 있다.)모든 창고 프레임은 시작과 끝이 있고 bp레지스터는 현재 창고 프레임의 시작 위치를 저장한다(변하지 않는다). sp는 창고 꼭대기의 위치를 가리키며 함수 운행에 따라 출고, 입고 작업이 발생하며 sp의 값은 이에 따라 변화한다.ip는 항상 다음 실행 중인 어셈블리 코드의 가상 주소를 가리킨다.호출 함수(caller)는 호출된 함수(callee)를 호출하기 전에 먼저 파라미터를 압축한 다음call 명령을 실행하면 CPU는 현재 과정의 다음 명령의 주소를 압축한다(이 단계는 CPU가 자동으로 실행하고 어셈블리 코드는 창고의 변화를 볼 수 없다). 호출이 되돌아올 때 계속 실행할 수 있도록 한다.로고 위치 레지스터 flag와 세그먼트 레지스터 cs도 저장합니다.callee가 실행되면 먼저 caller 창고 프레임의 bp를 압축하여 (되돌릴 때 회복해야 함) 현재 sp를 bp에 부여하여 이때의 bp가callee 창고 프레임의 시작 주소임을 나타낸다.그리고 캐럴러가 전송한 매개 변수를 꺼냅니다. (캐럴러 창고 프레임에서 bp 레지스터를 참고점으로 매개 변수를 가져옵니다. (bp+8), (bp+12)...)여기서 약간 곤혹스러울 수 있는 것은 (bp+0)인데 (bp+4)의 내용은 무엇입니까?위에서 말했듯이callee에 들어간 후 가장 먼저 해야 할 일은push%bp입니다. 이곳의 bp 레지스터는 이전 창고 프레임의 시작 위치에 저장됩니다.callee에 들어가려면 이전 과정의 되돌아오는 주소를 저장해야 합니다.이렇게 하면 (bp+0)의 값은 이전 과정의 창고 프레임 값이고 (bp+4)은callee가 되돌아온 후caller가 실행하는 다음 명령 주소임을 알 수 있다.이러한 함수 호출 규정을 통해 프로그램이 실행될 때의 매번 호출은 정확하고 틀림없을 수 있다.만약 반환 값이 있다면, 일반적으로ax 레지스터로 반환 값을 전달합니다. 그러면caller를 반환한 후에ax 레지스터의 값을 저장해야 합니다.

귀속


앞에서 함수를 호출한 벽돌을 던졌는데, 그 목적은 역시 귀속된 옥을 끌어내기 위해서였다.상술한 분석을 통해 피조 함수는 호출 함수 자체가 될 수 없고, 즉caller와callee는 같은 함수가 될 수 없다는 것을 알 수 있습니까?정답은, 아니오!호출 과정은 창고를 통해 유지되며, 창고는caller와callee의 중립 위치에 있습니다.논리적으로caller와callee가 같은 함수인지 여부는 창고에 있어서 다를 것이 없습니다. 모두 파라미터를 창고에 넣어야 합니다. caller에서 다음 명령의 주소를 창고에 넣어야 합니다. caller에서 파라미터를 꺼내서 판단을 하거나 (와) 실행한 다음 다음 다음 과정을 되돌려주거나 계속 호출해야 합니다.곱하기를 구하는 예에서 함수factorial은 먼저 n이 0인지 아닌지를 판단하고 만약에 1을 되돌려주지 않으면 n*factorial(n-1)을 되돌려준다. 이 표현에서 하위 표현식은factorial 자체를 호출한 다음에 n과 곱하는 것이다.그러면 먼저 서브표현의 값을 구해야 한다. 즉factorial 계산 매개 변수가 n-1일 때의 반환 값을 호출해야 한다.여기서 반환값이 x라고 가정하면 반환 후 먼저 n*x를 계산하고 이를 최종factorial 반환값으로 삼아야 한다고 예측할 수 있다.x를 얻으려면 n-1이 0인지 아닌지를 판단해야 한다. 0이 아니면 계속 호출해야 한다. 그리고 호출된 반환 값을 n-1과 곱해서 n이 0으로 줄어들 때까지 이 과정을 반복해야 한다.그리고 이전 콜러의 곱셈 동작을 점차적으로 실행하고 이전 콜러로 돌아갑니다.

총결산


함수 호출 과정에서 이 변조된 함수가 자신일 수 없다는 규정이 없고 귀속은 바로 함수 호출 중의 특례이다. 캐럴러와 캐럴러는 같은 함수이다.따라서 귀속 함수체는 호출 마감 판단을 포함해야 한다. 그렇지 않으면 무제한의 귀속 호출은 창고가 넘쳐 프로그램이 붕괴될 수밖에 없다.형식적으로 보면 귀속 함수는 두 가지 부분을 포함하는데 하나는 끝 판단(terminating cases)이고 하나는 귀속 호출(recursive cases)이다.factorial 함수의 Recursive case는 하나이며 선형 귀속에 속됩니다.여러 개의 recursive case가 있을 때 트리 귀속 (tree recursion) 이다.그러나 귀속 과정의 실질은 같고 대체적인 과정도 비슷하다.

좋은 웹페이지 즐겨찾기