117. 콜백

이번에는 함수 포인터를 콜백으로 이용하는 간단한 예를 살펴보아요.

정렬 알고리즘은 원소 형식이 무엇인지에 상관없이 대부분 같아요.
하지만 비교하는 것은 차이가 있죠.
만약 번호와 이름을 멤버로 갖는 학생 구조체 배열을 번호순으로 정렬한다면 멤버 번호로 비교해야 할 거예요.
이름순으로 정렬한다면 멤버 이름으로 비교해야겠죠.

이럴 때 비교하는 논리를 전달받아 형식에 관계없이 정렬하는 함수를 구현할 수 있어요.
이 때 비교하는 논리를 전달 받기 위해 함수 포인터를 사용해요.
호출하는 곳에서는 비교 함수를 정의하여 정렬 함수에 이를 입력 인자로 전달해요.
그리고 정렬 함수에서는 전달받은 비교 함수를 이용하여 정렬하는 것죠.
결국 피호출 함수인 정렬 함수에서 호출한 곳에서 정의한 비교 함수를 호출하는 것이므로 콜백이라 할 수 있어요.

typedef int (*Compare)(void *,void *); //비교한 결과를 반환하는 함수 포인터 정의

void Sort(void **base,size_t asize,Compare compare)
{
    ... 중략...
    if(compare(base[i-1], base[i])>0) // 앞의 요소가 더 클 때
    ... 중략 ...
}

int ComareByNum(Stu *stu1,Stu *stu2)
{
    return stu1->num - stu2->num;
}
int CompareByName(Stu *stu1,Stu *stu2)
{
    return strcmp(stu1->name,stu2->name);
}

◈ 콜백을 이용한 정렬 함수 구현 및 사용 예

#pragma warning(disable:4996)
#include <stdio.h>
#include <malloc.h>
#include <memory.h>
#include <string.h>
typedef int (*Compare)(void *,void *); //비교한 결과를 반환하는 함수 포인터 정의
typedef struct _Stu Stu;
#define MAX_NAME_LEN  20
struct _Stu
{
    int num;
    char name[MAX_NAME_LEN+1];
};
void StuSut(Stu *stu, int num,const char *name);
Stu *NewStu(int num,const char *name)
{
    Stu *stu = (Stu *)malloc(sizeof(Stu));
    StuSut(stu,num,name);
    return stu;
}
void StuSut(Stu *stu, int num,const char *name)
{
    stu->num = num;
    memset(stu->name,0,sizeof(stu->name));
    strncpy(stu->name,name,MAX_NAME_LEN);
}
void DeleteStu(Stu *stu)
{
    free(stu);
}
void StuView(Stu *stu)
{
    printf("번호:%d 이름:%s\n",stu->num,stu->name);
}
void BubbleSort(void **base,int asize,Compare compare)
{
   void *temp;
    int i = 0;
    for( ; asize>1 ;asize--) //정렬해야 할 사이즈가 1개 이상이라면 반복
    {
        for( i=1; i<asize ;i++)//비교해야 할 인덱스가 asize보다 작다면
        {
            if(compare(base[i-1], base[i])>0) // 앞의 요소가 더 클 때
            {
                //두 개의 요소를 교환
                temp = base[i-1];
                base[i-1] = base[i];
                base[i] = temp;
            }
        }
    }
}
int CompareByNum(Stu *stu1,Stu *stu2)
{
    return stu1->num - stu2->num;
}
int CompareByName(Stu *stu1,Stu *stu2)
{
    return strcmp(stu1->name,stu2->name);
}
 

int main()
{
    Stu *arr[4]={0,};
    int i = 0;
    arr[0] = NewStu(3,"홍길동");
    arr[1] = NewStu(11,"강감찬");
    arr[2] = NewStu(6,"김구");
    arr[3] = NewStu(8,"을지문덕");
    BubbleSort(arr,4,CompareByNum);
    printf("번호순\n");
    for(i=0;i<4;i++)
    {
        StuView(arr[i]);
    }
    printf("이름순\n");
    BubbleSort(arr,4,CompareByName);
    for(i=0;i<4;i++)
    {
        StuView(arr[i]);
    }
    for(i=0;i<4;i++)
    {
        DeleteStu(arr[i]);
    }
    return 0;
}

이 외에도 많은 곳에서 함수 포인터를 활용할 수 있어요.
여기서는 이 정도로 함수 포인터를 소개하고 마무리를 할게요.

결국 프로그래밍 실력은 문제 해결을 위한 자신의 고민과 스트레스 속에서 조금씩 성장하는 것 같아요.
작은 문제라도 자신이 직접 작성하는 것은 쉬운 일이 아니죠.
그리고 작은 문제라도 자신이 직접 해결하면 무언가 뿌듯함을 느낄 수 있을 거예요.
여러분 모두 훌륭한 개발자가 되길 기원할게요.
그 동안 수고했어요.