c언어 함수 포인터 인자는, 의외로 자주 볼 수 있습니다. 특히, 정렬이나 이진 탐색 method는 반드시 들어가는데요. 여기에서는 문법을 다루지 않습니다. 왜 쓰는지 알아보고, qsort case를 확장해서 이해해 보도록 하겠습니다.
c언어 함수 포인터
먼저, function pointer는, 함수를 가리키는 포인터입니다. 이게 무슨 소리인가? 함수는 실행 흐름입니다. 이걸 가리킬 수 있다는 것입니다. 아래 상황을 봅시다.
void (*t)(void) = f; 라고 되어 있어요. 함수 f와 g는 아무 것도 리턴하지 않고, 아무 인자도 받지 않습니다. 이 말의 의미는 아무 것도 리턴하지 않고, 아무 것도 받지 않는 함수를 가리키는 포인터 t가, f를 가리킨다는 의미입니다. 이 경우, t(); 문장은 함수 f를 호출합니다.
만약에, 이 t에 g를 대입하면 어떻게 될까요?
t가 g를 가리키게 되므로, t(); 문장은, f 함수를 호출하는 것이 아니라, g 함수를 호출합니다. 이는, t가 어떤 함수를 가르키는지에 따라 실행 흐름이 바뀔 수 있다는 의미입니다. 이제, 아래 예제를 보겠습니다.
먼저, 아무 것도 리턴하지 않고, 아무런 인자도 받지 않는 함수 f와 g가 있습니다. 그리고, 3번째 func 함수는, 아무것도 리턴하지 않고, 아무런 인자도 받지 않는 함수를 가리키는 포인터를 넘깁니다.
func 함수는, 함수 포인터 work를 받아, work를 실행합니다. 이 work는 func가 결정하지 않습니다. func가 받았다는 것이 중요해요. 즉, func 내에서 실행하고 싶은 실행 흐름을, func 내부에서 받은 게 아니라 외부에서 받은 것이 매우 중요합니다.
이렇게 되면, 어떤 것이 유리해 질까요? 현재 Hello f를 출력하게 하고 있어요. 이를, Hello g를 출력해게끔 바꾸고 싶다고 해 봅시다. 그런데, 우리는 work를 받게 했단 말이죠.
이를 그리면 위와 같습니다. func는 user가 바꿀 수 없는 함수라고 해 보겠습니다. 그래서 회색으로 표시했습니다. func는 work를 받습니다. 즉, func는 work에 의존하게 됩니다. 현재 Hello f를 출력하게 했다면, 상황이 아래와 같았을 겁니다.
work가 함수 f의 흐름을 받았습니다. 그리고 이 work를 func가 의존하게 됩니다. 따라서 work(); 에서, 함수 f가 호출되어 Hello f가 출력됩니다. 이것을 user가 호출하는 곳에서 g로 바꿔버리면 어떻게 될까요?
work는 함수 g의 흐름을 받았습니다. 따라서, work(); 에서, 함수 g가 호출되어 Hello g가 출력됩니다. 중요한 것은 무엇일까요? 우리는, func를 바꾸지 않고도, func 내부의 work가 가리키는 함수를 바꾸었습니다. 그래서, 흐름을 바꿀 수 있었다는 것입니다. 결과를 보겠습니다.
Hello f와 Hello g가 차례대로 출력됩니다.
c언어 qsort 함수
qsort 함수의 원형은 아래와 같습니다.
여기서 중요하게 보아야 할 것은 4번째 인자입니다.
- int (*compare)(const void*, const void*)
const void * 2개를 받고, int형을 돌려주는 함수 포인터를 받겠다는 의미입니다. 왜 이게 들어갔는가? 대부분의 sort는 키 값 2개를 비교합니다. const void * type의 2개는 키 2개를 의미합니다. 그런데, 우리는 정렬을 여러 기준으로 하게 됩니다. 예를 들어, 오름차순, 내림차순 등입니다.
2번 프로그램을 봅시다. qsort를 호출해서 정렬하는 것입니다. cmp1과 cmp2는 key 2개를 비교하는 것인데요. a의 값과 b의 값을 비교했을 때 음수가 나오면 a가 b보다 앞에 나옵니다. 0보다 큰 수가 나오면 뒤에 나오게 됩니다.
그러므로, cmp1은 오름차순으로, cmp2는 내림차순으로 정렬하기 위한 key를 비교하는 것임을 알 수 있어요. 상황은 아래와 같이 그려집니다.
오름차순 정렬을 하기 위한 키 값 비교 함수가 qsort에 inject 되었습니다. 결과는 어떻게 나올까요?
오름차순으로 정렬됩니다. 만약에, 내림차순으로 정렬하고 싶으면 어떻게 하면 될까요? qsort 내부를 바꿔야 할까요? 아닙니다.
단지, 내림차순으로 정렬하기 위한 키 값 비교 함수로 바꾸면 됩니다. qsort는 제가 정의한 키 값 비교 함수를 씁니다. 따라서, 정렬 기준이 바뀌더라도 qsort가 바뀔 일이 없습니다. 그러면 예제 2번에서 어느 부분만 바꾸면 될까요?
qsort의 4번째 인자만 바꾸면 됩니다.
cmp1 대신에 cmp2로 바꾼 것을 볼 수 있어요. 이제, 결과를 볼까요?
아까와는 다르게 내림차순으로 정렬되었음을 알 수 있습니다. 즉, qsort 함수는 두 키를 비교하는 흐름을 user가 넘겨주는 것에 따라 바꾸는 것입니다.