오늘은 C언어에서 빼먹은 포인트 함수에 대해서 이야기를 하도록 하겠습니다. 함수에도 주소가 있습니다. 그래소 호출 함수를 포인트 함수로 저장하게 되면 포인트 함수를 통해 해당 함수를 호출 할 수 있습니다. 포인트 변수의 개념을 잘 생각해보시면 그 변수의 개념을 포인트 함수의 개념으로 접근하시면 어느정도 이해가 되 실거에요. 그냥 호출하고자 하는 함수를 일반 사용자 정의함수로 정의하고 해당 사용자 정의함수를 호출하여 쓰면 되는데 굳이 포인트 함수를 사용하느냐 생각 되실 수 있지만 포인트 함수는 꽤 유용한 함수입니다. 포인트 함수는 특정 하나의 함수만 호출해 올 수 있는 것이 아니라 서로 다른 다양한 함수를 하나의 포인트 함수로 호출하여 사용 할 수 있습니다. 이 부분이 좀 어려울 수 있으니 본격적으로 설명할 때 예제를 들어 설명하겠습니다. 암튼 포인트 함수는 하나 이상의 일반 사용자 함수를 호출 할 수 있고 코딩에서도 크게 변경하여 수정하는 코딩을 줄일 수 있어 함수로 꽤 유용한 함수임으로 잘 알아 두시기 바랍니다.
그럼 포인트 함수에 대해 알아보도록 하죠.
아래 예를 들어 알아보도록 하죠.
int add(int a, int b){
return a+b;
}
int (*Cal)(int, int);
Cal=add;
위 코딩을 보시면 사용자 정의 함수인 add()함수가 있습니다. 그리고 포인트 함수로 *Cal(int, int)라는 함수가 선언 되었습니다. 아직 할당이 되어 있지 않는 빈 껍질의 포인트 함수입니다.
Cal=add;
이렇게 하면 포인트 변수에서 주소지는 포인트 변수에 저장하는 것처럼 포인트 함수도 add()함수의 포인트 주소값을 Cal()함수에 할당이 됩니다. Cal() 포인트 함수는 add()라는 함수가 할당 된 것이죠. 이름은 Cal()함수이지만 실제 동작은 add()함수가 호출되어 처리가 됩니다. 어떤 느낌인지 아시겠지요. 포인트 변수와 의미적으로 같습니다. 변수의 주소를 넘기나 함수의 주소를 넘기나 같은 의미인 셈이죠. 쉽게 말해서, add()라는 함수에 대한 새로운 이름이 생긴 것이죠. add()함수를 호출하든지 아니면 Cal()함수를 호출하든지 둘중 어느 함수를 호출해도 같은 결과를 얻는 셈이죠.
포인트 함수는 굳이 이렇게 새로운 이름을 지을 필요가 있는가? 하고 의문이 생길 거에요.
포인트 함수가 유용한 이유를 이제 설명 하겠습니다.
예)
int add(int a, int b){
return a+b;
}
int sub(int a, int b){
return a-b;
}
int (*Cal)(int, int);
Cal=add; //덧셈
Cal(1,2);
처리문1;
Cal=sub; //뺄셈
Cal(1,2);
처리문2;
위 코딩을 잘 보십시오. 어떤 느낌이 드시나요. 첫번째 Cal(1,2)와 두번째 Cal(1,2)의 값은 다릅니다. 그 이유가 뭘까요.
첫번째 Cal() 포인트 함수에는 add()함수 주소가 할당 되었고 두번째 Cal() 포인트 함수에는 sub()함수 주소가 할당 됩니다. 포인트 변수나 함수의 특징 중 하나는 중간에 포인트가 가리키는 주소를 변경 할 수 있다는 것이죠. 처음 포인트 함수를 소개 할때 하나의 포인트 함수는 하나 이상의 일반 사용자 정의함수들을 호출 할 수 있다고 했죠. 위 코딩처럼 add(), sub()함수들을 하나의 Cal()이라는 포인트 함수에서는 호출 할 수 있습니다. 그래서 Cal() 함수가 상황에 따라 add()함수 동작을 하거나 sub()함수 동작을 수행 할 수 있게 되는 것이죠.
포인트 함수는 하나 이상의 함수들을 호출 할 수 있습니다. 이게 포인트 함수를 쓰는 이유이기도 하죠. 메인 함수에서 하나의 Cal()이라는 포인트 함수로 코딩을 해 나가면 나중에 add(), sub()함수를 호출하도록 코딩했다가 다시 mul(), div()함수를 추가 할 경우 메인 함수에서 사용한 Cal()이라는 함수를 수정 할 필요 없이 호출 할 수 있게 해당 함수들의 주소만 던져주면 됩니다. 즉, 하나의 틀을 만들어 놓고 추가 할 때 기존의 함수를 그대로 유지 한 상태에서 추가하고자 하는 함수만 추가하면 기존의 포인트 함수로 호출 할 수 있게만 수정하면 크게 코딩을 변경하지 않고 간단히 코딩을 수정 할 수 있습니다.
그럼 포인트 함수를 코딩해 보고 그 결과를 통해서 포인트 함수를 이해해 볼까요.
[전체소스]
#include
#include
float ADD(float a, float b){
return a+b;
}
float SUB(float a, float b){
return a-b;
}
float MUL(float a, float b){
return a*b;
}
float DIV(float a, float b){
return a/b;
}
int main(int argc, char *argv[]) {
float a,b;
char op;
float total;
float (*Cal)(float,float);
scanf("%f %c %f",&a,&op,&b);
switch(op){
case '+': Cal=ADD; break;
case '-': Cal=SUB; break;
case '*': Cal=MUL; break;
case '/': Cal=DIV; break;
}
total=Cal(a,b);
printf("%0.0f %c %0.0f = %0.1f\n",a,op,b,total);
system("pause");
return 0;
}
위 코딩은 Cal()이라는 포인트 함수를 이용하여 사칙연산을 수행하는 프로그램을 만들었습니다. 각각 ADD, SUB, MUL, DIV의 주소를 Cal이라는 포인트 함수에 할당함으로서 해당된 연산을 수행 하게 됩니다. 이렇게 하나의 포인트 함수를 통해 서로 다른 함수들을 호출하여 계산을 수행 할 수 있는 간단하 코딩을 수행 하였습니다. 아마 위 코딩만으로 보면 그냥 각 연산을 다음과 같이 switch()함수에서 선택되면 그 선택 된 함수를 다이렉트로 호출하여 계산하여 결과를 total 변수에 저장하면 되지 않느냐 하고 생각 하실 수 있습니다.
switch(op){
case '+': total=ADD(a,b); break;
case '-': total=SUB(a,b); break;
case '*': total=MUL(a,b); break;
case '/': total=DIV(a,b); break;
}
이렇게 말이죠. 지금 사칙연산을 주 핵심이 아니라 포인트 함수를 사용하고 그 사용을 했을 때 의미를 이해하는 코딩이기에 사칙연산 최적화가 아닙니다. 암튼 이 코딩도 계속 계산하는 함수들이 추가되면 해당 함수들을 일일히 변수 인자까지 다 코딩해야 하지만 외부 라이브러로 서언된 함수들을 그 곳에서만 수정 변경하여 코딩해 놓고 방금 실험한 코딩의 주소 던지기로 간략화 코딩을 하는게 더 효율적인 코딩입니다.
결론은 포인트 함수는 알맹이가 없는 하나의 틀로 생각하시면 됩니다. 그 알매이는 해당 호출 함수로 할당이 되고 포인트 함수는 할당된 호출 함수의 동작을 수행한다는 점을 잘 이해해 주시기 바랍니다.
[결과]
결과는 나눗셈으로 정상적으로 DIV()함수가 선택이 되어 Cal() 포인트 함수에 할당이 되고 나눗셈을 수행한 결과가 콘솔창에 출력 되었네요.
포인트 함수의 의미를 잘 이해해 주시기 바랍니다. 포인트함수로 콜백함수를 구현 할 수 있는 아주 유용한 것이기 때문입니다. 포인트 함수를 이해 할 때는 포인트 변수 개념으로 이해하시면 보다 쉽게 이해 할 수 있습니다.
그리고 위 소스에서 추가로 무한루프문 while(1) 문으로 해서 계속 숫자를 입력 받아서 계산할 수 있는 계산기로 업그레이드를 해보세요.