본문 바로가기

2학년/C

[C]함수(2)

매개변수의 호출방식

#include <stdio.h>

int add(int n, int m){
     return n + m;
}


int main(void){
    int a =3; b=5;
    printf("%d", add(a,b));
}

add() 함수의 주소는 컴파일 타임에 스택메모리에 적재되어 있었을 것이다. 순서대로 인수 a와 b가 아닌 add함수 내부의 지역변수 n과 m이 연산의 주체가 된다. 

따라서 n에는 3, m에는 5가 저장되어 있고, return은 n+m 이므로 8이 출력된다. 

 

 함수의 인수(argument)와 매개변수(parameter) 

  위의 예시에서 int n, int m 은 매개변수인 반면에 a,b는 인수이다. 

 인수란 '값, 변수, 참조등에 전달되는 원본 값'이고

 매개변수란 '인수로부터 전달받은 값을 저장하는 함수 내의 지역변수'이다. 

 

 함수 내부에선 인수를 알지 못한다. 변수의 유효범위를 생각해보면 main함수와 add 함수는 서로 다른 블록에 존재하고, main함수에서 add에 전달하는 인수 a, b는 지역변수이므로 add쪽 블록에선 접근 자체가 불가능하다. 

따라서 우리가 함수에서 실행하는 모든 연산은 사실 스택프레임을 호출할 때, 인수의 값을 매개변수에 초기화하여 매개변수를 통한 연산 작업을 수행하는 것이다.

 C언어에서 함수를 사용하면서 인수를 매개변수에 전달할 땐, 오직 Call by value (값에 의한 참조) 방식만이 지원된다. 인수의 값을 매개변수에 복사하고, 실제로 매개변수가 계산되는 것이다. 하지만 매개변수는 지역변수이기 때문에 함수 블록을 벗어나자마자 스택 메모리에 할당받았던 메모리 공간을 반환한다. 즉, 함수 내부에서 일어나는 일들은 인수의 원본값과는 아무런 연관이 없는, 복사값을 통한 연산이기에 인수는 아무런 변화도 일어나지 않는다. 

 

따라서, 함수를 통해 인수의 값을 바꾸려면 '포인터'를 이용해야 한다.

#include <stdio.h>

void plus(int* ptr) {
    *ptr +=10;
}

int main(void){
    int a=3, b=3;
    plus(&a);
    b+=10;
}

 

plus 함수는 a의 주소를 인수로 받아 매개변수 ptr에 전달하고, ptr은 변수 a의 주소값을 내부적으로 저장하고 있다.  포인터 변수 ptr 또한 지역변수이므로, 함수가 종료되면 메모리상에서 반환된다. 하지만 포인터 변수 ptr이 가진 주소값을 *(참조연산자)를 통해, 시스템 메모리상에서 해당 주소에 직접 찾아가 저장된 값을 수정하는 것이 가능하다. 

 

이렇듯 매개변수를 통해 인수의 원본 데이터 값을 수정할 수 있도록 포인터를 인수로 저장하는 방식을 Call by address(주소에 의한 참조)라고 부른다.  

 

      *Call by reference와의 차이 :  함수의 인수로 값을 복사하여 매개변수에 전달하는 것이 아닌, 해당 변수에 참조자를 전달하는 방식 (Call by adress는 이 기능을 흉내낸 것이고 사실상 Call by value랑 동일한 것이다)

 

 

재귀 함수 

 재귀호출

 함수가 자기 자신을 함수 내부에서 다시 호출하는 것을 의미한다.

 

 재귀에서 주의해야할 점 

  재귀호출은 함수 안에 함수를 호출하는 과정에서 기존의 스택프레임이 그대로 남아 있게 된다. 이 과정에서 재귀호출의 깊이가 길어질수록 스택프레임의 부하가 심각해진다. 스택프레임이 모두 차버리면 스택 오버플로우로 모든 프로세스가 중단된다.

 

 스택 오버플로우

  스택영역에 메모리를 할당받을 수 없는 경우에 발생하는 런타임 에러이다. 

 

 

함수 포인터 

  프로그램에서 정의된 함수는 실행될 때 모두 메인 메모리에 올라가게 된다. 이때 함수의 이름은 메모리에 올라간 함수의 시작주소를 가리키는 포인터 상수가 되고 주소값을 가지기 때문에 포인터 변수에 주소값을 저장할 수 있다. 

 

 함수 포인터 선언

   원형 : void func(int,int);

   함수 포인터 : void(*ptr_func)(int,int);

  함수 포인터의  선언은 주소를 저장할 대상 함수의 반환형과 매개변수는 완전히 동일하고, (*포인터명)꼴로 선언할 수 있다. (우선 순위상 반드시 소괄호로 감싸야한다)

 

 함수와 구조체 

 함수의 인수나 구조체를 사용할 수 있다. 

 구조체 자료형도 기본 자료형의 경우와 마찬가지로 Call by value 방식으로 인수의 값이 매개변수로 복사되어 전달될 뿐이다. 하지만 구조체를 그대로 인수로 전달하면 구조체 변수의 크기가 워낙 크기 때문에 데이터 전달 및 프로세스 처리 속도가 느려진다. 

 

함수의 선언부에서 매개변수 자리에 const 키워드를 사용하면 함수 내에서 메모리 내부에 접근하여 값을 변경할 수 없도록 제한할 수 있다.

  const 매개변수는 주소값을 통해 참조는 가능하나 함수 내부의 값은 상수화되어 수정할 수 없게 된다.  즉, 구조체의 주소만 전달하므로 접근 속도 및 처리속도가 빠르고, 구조체 원본에 대한 참조도 가능하지만 수정만 할 수 없게 되는 것이다. 

 

   

 

  

'2학년 > C' 카테고리의 다른 글

[C]문자와 문자열(2)  (0) 2023.08.02
[C]문자와 문자열(1)  (0) 2023.08.01
[C]함수(1)  (0) 2023.07.30
[C]사용자 지정 타입  (0) 2023.07.29
[C]사용자 지정타입(1)  (0) 2023.07.28