기존에 알고 있는 포인터는 데이터 세그먼트 or 스택 세그먼트에 있는 변수의 주소나 메모리 주소를 저장해서 사용하는 데이터 포인터에 대한 얘기다.
이번 시간은 코드 세그먼트에 있는 명령문의 주소를 저장해서 포인터로 사용하는 방법에 대해 알아보자.
함수 포인터
int sum(int a, int b)
{
int result = 0;
result = a + b;
return result;
}
int result = sum(2,3);
int sum(int a, int b); == int(*p)(int, int); //*p로 sum함수를 호출한다.
p = ∑ //sum의 함수의 주소를 p에 저장한다.
함수의 포인터를 사용하는 이유는 무엇일까?
-> 같은 형식의 함수를 그룹으로 묶을 수 있기 때문이다.
같은 형식이란(매개변수, 자료형, 같은 형태의 반환값)이다.
다음 예제를 보자.
<덧셈>
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;
}
==============================
int result 1, result2, result3,result4;
result1 = sum(8,2);
result2 = sub(8,2);
result3 = Mul(8,2);
result4 = Div(8,2);
이 4개의 함수를 포인터 하나로 묶을 수 있다.
int (*p[4])(int , int) = {&sum, &sub, &Mul, &Div};
여기서 *p([4]) 와 (*p)[4]의 차이를 모른다면 포인터 부분을 다시 공부하도록 하자.
==========================================================
int (*p[4])(int int) = {&sum, &sub, &Mul, &Div};
int result[4], i; // 함수를 호출 했을 때 반환되는 값 4개를 저장할 배열을 선언
for(i = 0; i < 4; i++) result[i] = (*p[i])(8,2);
#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 table[4] = { '+', '-', '*', '/' };
for (int i = 0; i < 4; i++)
{
printf("%d %c %d = %d\n", 8, table[i], 2, (*p[i])(8, 2));
}
}
콜백 함수
예를 들어 두 개의 정수 값을 넘겨 받아서 합산하는 sum 함수를 라이브러리 형태로 제공한다고 생각해보자.
-라이브러리 프로그래머가 만든 헤더 파일과 라이브러리 파일
<sum함수의 원형>
sum.h
1. int sum(int a, int b);
<두 값을 합산하는 함수>
sum.lib
int sum(int a, int b)
{
return a + b;
}
-라이브러리 사용자가 사용하는 형태
#include "sum.h"
#pragma commnet(lib, "sum.lib")
void main()
{
int result = sum(2,3);
}
※pragma 전처리기를 사용하면 컴파일러의 여러 가지 설정 값을 수정할 수 있다.
위에서 사용한 형식은 sum.lib 파일을 이 프로그램에서 사용하겠다는 의미다.
만약에 프로그래머에게 두 숫자 값이 음수인 경우에 양수로 변환해서 합산하는 함수도 추가로 만들어 달라고 요청했다고 해보자. sumabs라는 새로운 함수를 추가해야된다.
-라이브러리 프로그래머가 만든 헤더 파일과 라이브러리 파일
<sum함수의 원형>
sum.h
1. int sum(int a, int b)
2. int sumabs(int a, int b)
sum.lib
int sum(int a, int b)
{
return a + b;
}
int sumabs(int a, int b)
{
if(a<0) a = a * (-1);
if(b<0) b = b * (-1);
return a + b;
}
-라이브러리 사용자가 사용하는 형태
#include "sum.h"
pragma commnet(lib, "sum.lib")
void main()
{
int result1, result2;
result1 = sum(2,-3);
result2 = sumabs(2, -3);
}
라이브러리에 포함된 함수는 본래의 기능을 유지하고 사용자가 원하는 경우에 스스로 함수의 기능을 일부 수정할 수 있도록 제공 하는 것이 좋다.
그렇다고 프로그래머가 라이브러리 소스 코드 전체를 줄 수는 없다. 그러면 라이브러리 사용자는 어떻게 소스 코드 없이 라이브러리를 수정해서 사용할 수 있을까?
함수를 매개변수로 함수 포인터를 사용하면 된다.
void myabsolute(int *p)
{
if(*p<0) *p = (*p) * (-1);
}
int sumabs(int a, int b)
{
myabsolute(&a); // if (a<0) a = a * (-1)
myabsolute(&b); // if (b<0) b = b * (-1)
return a + b;
}
void (*p)(int *); 함수의 주소를 받아서 사용할 수 있다.
================================================
int sumabs(int a, int b, void (*fp_abs)(int*))
{
(*fp_abs)(&a);
(*fp_abs)(&b);
return a + b;
}
=================================================
sumabs(5,-1, &absolute); //sumabs함수에서 myabsolute 함수를 호출한 것과 같음
sum(5, -1, NULL); //sumabs 함수에서 myabsolute 함수를 사용하지 않는 경우
==================================================
<NULL에 대한 예외처리>
int sumabs(int a, int b, void (*fp_abs)(int *))
{
if(fp_abs != NULL) (*fp_abs)(&a);
if(fp_abs != NULL) (*fp_abs)(&b);
return a + b;
}
함수 포인터를 이용해서 a,b 변수마다 다르게 사용할 수 있도록 설정하겠다.
-라이브러리 프로그래머 시점
<함수 원형>
-sum.h-
1. int sum(int a, int b. void(*pa)(int *), void(*pb)(int *))
-sum.lib-
int sum(int a, int b, void (*pa)(int *), void (*pb)(int *))
{
if(NULL != pa) (*pa)(&a);
if(NULL != pb) (*pb)(&b);
return a + b;
}
=============================================================
#include "sum.h"
#pragma comment(lib, "sum.lib")
void main()
{
int result = sum(-3, -2, NULL, NULL);
}
#include "sum.h"
pragma commnet(lib, "sum.lib")
void myabsolute(int* p)
{
if (*p < 0) *p = (*p) * (-1);
}
void main()
{
int result = sum(-3, -2, myabsolute, NULL);
}
==============================================================
-라이브러리 프로그래머 시점
<함수 원형>
-sum.h-
1. int sum(int a, int b. void(*pa)(int *), void(*pb)(int *))
===============================================================
-sum.lib-
int sum(int a, int b, void (*pa)(int *), void (*pb)(int *))
{
if(NULL != pa) (*pa)(&a);
if(NULL != pb) (*pb)(&b);
return a + b;
}
==============================================================
※호출관계를 파악해보자.
sum(-3,-2,myabsolute, NULL) <==>
int sum(int a, int b, void(*pa)(int *), void (*pb)(int *))
1. sum 함수의 pa변수가 NULL이 아니기 때문에 if(NULL != pa)(*pa)(&a); 문장이 수행된다.
2. sum의 pa 변수 == myabsolute // (*pa)(&a) == myabsolute(&a)
3. sum 함수의 매개변수 a의 주소 값이 myabsolute 함수의 포인터 변수 p에 저장된다.
4. myabsolute 포인터 변수인 p는 변수 a의 주소 값을 저장하고 있다.
5. 따라서 p가 가리키는 값은 음수인 -3이라서 if(*p<0) *p = (*p) * (-1); 조건문이 실행된다.
6. 함수의 포인터 pb는 NULL 값이 저장되어 있기 때문에 if(NULL!=pb) (*pb)(&b); 조건문은 실행 x
7. sum함수의 변수 a값이 -3에서 3으로 변경되었기 때문에 3 + (-2) 가 수행 -> 1이 반환