[C] 함수 포인터와 함수 그룹

김태희·2023년 11월 29일
0
post-thumbnail

일이 바빠 며칠만에 포스트를 게시한다.

그래도 그 사이에 당직을 서면서 자료구조 책을 많이 훑어보고 개념을 좀 이해하는 시간도 가질 수 있었다.

이 글과 콜백 함수를 다루는 다음 포스트 이후부턴 본격적으로 자료구조와 알고리즘을 시작할 것이다.

함수 포인터

프로세스 메모리 영역에 저장되는 명령

C언어 소스코드가 컴파일러를 거치면 실행 파일이 되고 실행 파일은 CPU가 처리할 수 잇는 '기계어 명령문' 단위로 이뤄진다.

프로그램이 실행되면 프로세스 형태로 메모리에 저장되는데 이때 명령문들은 코드 세그먼트(CS, Code Segment)에 옮겨진다.

이 원리에 따라 코드 세그먼트에 저장된 각 명령문들도 변수처럼 주소를 갖게된다. 이로 인해 포인터 개념을 명령문에도 사용할 수 있다.

프로그램이 운영체제로부터 할당받는 대표적인 메모리 공간(RAM)

출처 : https://velog.io/@hidaehyunlee/%EB%A9%94%EB%AA%A8%EB%A6%AC-%EA%B5%AC%EC%A1%B0%EB%A5%BC-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90

하지만 메모리의 위치를 임의로 이동하면서 명령문을 실행하면 함수 단위로 구역을 나눠서 사용하는 스택 프레임이 엉망이 되기에 함수 단위로만 이동해야한다.

스택 프레임이란 ?

지역 변수와 매개변수가 저장되는 영역인 스택 세그먼트는 함수의 호출과 함께 할당되며, 함수의 호출이 완료되면 소멸한다.

함수가 호출되면 스택에는 함수의 매개변수, 호출이 끝난 뒤 돌아갈 반환 주소값, 함수에서 선언된 지역 변수 등이 저장된다.

이렇게 스택 영역에 차례대로 저장되는 함수의 호출 정보를 스택 프레임(stack frame)이라고 하고 스택 프레임 덕분에 함수의 호출이 모두 끝난 뒤에, 해당 함수가 호출되기 이전 상태로 되돌아갈 수 있다.



함수 포인터란 ?

특정 함수를 구성하는 시작 명령의 위치를 가리키는 포인터이다.

int Sum(int a, int b){
  int result = 0;
  result = a+b;
  return result;
}

위와 같은 함수의 첫 번째 명령문의 주소값을 얻고자 할 때
int result = 0; 가 저장된 메모리 위치의 주소가 Sum 함수의 시작 주소가 된다.

하지만 int result = 0; 코드는 다른 함수인 Sub 함수에서도 똑같이 사용될 수 있기에 함수를 구별하는 기준으로 삼는것은 적합하지 않다.

따라서 C는 함수의 첫 번째 명령문이 아닌, 함수 이름 앞에 &을 적음으로써 함수에 포함되는 첫 번째 명령문의 주소 값을 얻을 수 있도록 한다.

Sum 또는 ∑ //Sum 함수의 첫 번째 명령문 주소를 의미함(& 생략 가능)



함수의 주소값으로 함수 실행하기

주소 값을 저장할 수 있는 포인터를 선언해야한다. 함수 포인터는 함수 원형을 사용해서 포인터를 선언한다.
int Sum(int, int); 형인 Sum 함수로 예를 들면 아래와 같이 사용할 수 있다.

int (*p)(int int);
p = ∑ //혹은 p = Sum;
int result = (*p)(2,3);



함수 그룹

함수 포인터는 주로 라이브러리의 콜백 함수와 같은 형식의 함수를 그룹을 묶을 때도 사용한다.

같은 수의 매개변수와 자료형, 같은 형태의 반환값을 갖는 함수들을 같은 형식의 함수라고 한다.



함수 포인터를 사용하여 비슷한 함수를 반복문으로 호출하기

#include <stdio.h>

int Sum(int a, int b){
  return a+b;
}
int Sub(int a, int b){
  return a-b;
}
int Mul(int a, int b){
  return a*b;
}
int Div(int a, int b){
  return a/b;
}

void main(){
  int(*p[4])(int, int) = {Sum, Sub, Mul, Div};
  char sign[4] = {'+', '-', 'x', '/'};
  for(int i = 0; i<4; i++){
    printf("%d %c %d = %d\n", 50, sign[i], 45, p[i](50, 45));
  }
}


결과 

50 + 45 = 95
50 - 45 = 5
50 x 45 = 2250
50 / 45 = 1

이와 같이 포인터를 배열로도 선언해(포인터를 배열 개수만큼 만드는 것과 같음) 같은 형식의 함수를 묶어 사용할 수 있다.

int *p[4]와 int (*p)[4]의 차이 복습

전자는 포인터 변수 p를 4개 선언한것과 같음

후자는 int [4]형 변수를 가리키는 포인터 변수 p를 선언

다음 포스트에서는 콜백 함수에 대해 다루겠다.

0개의 댓글