이번에는 함수 호출에서 종료까지 수행 원리를 간단히 알아볼게요.
함수를 호출하면 해당 함수의 지역 변수를 위한 메모리를 스택에 할당해요.
그리고 피호출 함수의 동작이 끝나면 호출한 함수의 다음 부분을 수행하는 것이 기본 동작이죠.
main 함수가 시작하면 main함수의 지역 변수를 위한 메모리를 스택에 할당해요.
그리고 Add 함수를 호출하면 입력 인자를 복사한 후에 Add 함수의 지역 변수를 할당한 후에 복사한 값으로 초기화를 수행하죠.
그리고 함수의 return 문을 만나면 자신의 스택 영역 바로 밑(호출 함수 스택의 맨 위)에 반환 값을 설정해요.
그리고 자신의 스택 메모리를 해제한답니다.
호출 결과를 대입하는 구문에서는 자신의 스택 맨 위에 값(피호출 함수에서 return 문에 의해 설정한 값)으로 설정하는 거예요.
매개변수 전달 방법
매개 변수를 전달하는 방법에는 값에 의한 전달(Call By Value)과 참조에 의한 전달(Call By Reference)이 있어요.
값에 의한 전달은 호출한 함수에서 전달한 인자의 값을 복사하여 피호출 함수의 입력 매개 변수의 초기값을 설정하는 방식이죠.
이러한 전달 방식에서는 호출한 곳의 인자와 피호출한 곳의 인자의 값은 같지만 메모리는 독립적이예요.
참조에 의한 전달은 호출한 함수에서 전달한 인자를 피호출 함수의 입력 매개 변수가 참조하여 사용하는 방식이죠.
이러한 전달 방식에서는 호출한 곳의 인자와 피호출한 곳의 인자는 같은 개체예요.
C언어에서 매개 변수를 전달하는 방식은 값에 의한 전달 방식을 사용해요.
일부에서는 포인터 변수처럼 메모리 주소를 값을 갖는 데이터를 인자로 전달하는 것을 “주소에 의한 호출(Call By Memory)”이라 부르기도 한답니다.
그렇지만 주소에 의한 호출도 포인터 변수가 갖고 있는 값이 메모리 주소인 것이므로 이 또한 값에 의한 호출 방식이예요.
즉 C언어에서 함수 호출 방식은 값에 의한 호출이며 메모리 주소를 값으로 갖는 표현을 인자로 전달하는 것을 일부에서 주소에 의한 호출이라고 세분화시킨 것일 뿐이예요.
하지만 이처럼 세부화를 한 이유를 아는 것은 C언어로 프로그래밍할 때 매우 중요할 수 있어요.
만약 호출하는 곳에 있는 두 개의 변수가 갖고 있는 값을 바꾸는 함수를 만들어 사용한다고 가정할게요.
C 언어에서 함수 호출은 입력 인자로 전달한 값을 복사하여 전달하죠.
따라서 호출하는 곳에서 전달한 변수와 피호출 함수의 변수는 서로 다른 메모리예요.
피호출 함수에서 전달받은 전달받은 두 변수의 값을 바꿔도 호출한 함수의 변수의 값은 아무런 변화도 없어요.
C 언어의 지역 변수의 가시성은 선언한 블록 내에서만 유효해요.
◈ 입력 매개변수 전달 원리를 확인하는 예
#include <stdio.h> void Swap(int a,int b); int main() { int i = 2; int j = 3; printf("[main] before i:%d j:%d\n",i,j); Swap(i,j); printf("[main] after i:%d j:%d\n",i,j); return 0; } void Swap(int a,int b) { int temp = 0; printf("[Swap] before a:%d b:%d\n",a,b); temp = a; a = b; b = temp; printf("[Swap] after a:%d b:%d\n",a,b); }
◈ 실행 결과
[main] before i:2 j:3 [Swap] before a:2 b:3 [Swap] before a:3 b:2 [main] after i:2 j:3
C언어에서 두 개의 변수의 값을 바꾸는 함수를 만들려면 입력 매개 변수를 포인터 형식으로 정해야 한답니다.
호출하는 함수에서 바꾸어야 할 두 변수의 메모리 주소를 전달하는 거예요.
피호출 함수에서는 전달받은 메모리 주소를 간접 연산으로 접근하여 값을 바꾸는 거죠.
◈ 두 수를 바꾸는 함수
#include <stdio.h> void Swap(int *a,int *b); int main() { int i = 2; int j = 3; printf("before i:%d j:%d\n",i,j); Swap(&i, &j); printf("after i:%d j:%d\n",i,j); return 0; } void Swap(int *a,int *b) { int temp = 0; temp = *a; *a = *b; *b = temp; }
◈ 두실행 결과
before i:2 j:3 after i:3 j:2
*일부에서는 포인터 변수는 데이터를 저장하는 것이 아니라 데이터를 위치를 가리키는 것이어서 포인터 형식을 입력 인자로 전달하는 것을 참조에 의한 호출(Call By Reference)라고 말하기도 해요.
이는 OOP적인 관점에서 바라본 것이예요.
지금은 이러한 설명이 혼란만 가중할 것임을 알지만 그 또한 우리 모두의 몫이겠죠.