본문 바로가기
프로그래밍/C++

C++ 함수 포인터란? 왜 사용할까?

by Hwan2 2019. 7. 20.
반응형

 

 

 

 

C와 C++에서는 포인터라는 중요한 요소가 존재합니다.

 

그리고 포인터를 많이 활용하죠.

 

그 이유는 주소 값을 대입해 해당 위치로 이동하거나 접근이 가능하기 때문입니다.

 

함수 또한 그 주소 값을 갖고 호출이 되면 해당 주소 값으로 이동해 프로그램을 실행하게 됩니다.

 

함수 포인터란 함수의 주소 값을 지닐 수 있는 포인터 변수 입니다.

 

선언 방법은 다음과 같죠.

 

int (*fp1)();
int (*fp2)(int);
int (*fp3)(int, int);
bool (*fp4)();
void (*fp5)();
 

 

 

위와 같은 형식으로 정의할 수 있습니다.

 

반환 타입은 말 그대로 함수의 반환 타입.

 

변수 이름은 함수를 저장할 수 있는 변수의 이름

앞에 *이 붙었는데 이는 포인터 변수이기 떄문입니다. 설명을 덪붙이자면

함수 포인터도 결국 포인터 변수. 즉, 주소 값을 저장해야 하기 때문에 포인터로 선언해 줘야 합니다.

 

 

매개 변수는 함수의 매개 변수의 갯수와 타입 입니다.

 

 

 

 

간단한 예제를 보여드리겠습니다.

 

#include<iostream>
#include<cstdio>

void fnc_1() {
    printf("fnc 함수\n");
}

void fnc_2(int n) {
    printf("%d\n", n);
}

void fnc_print(void(*fnc)()) {
    fnc();
}

void fnc_print2(void(*fnc)(int), int n) {
    fnc(n);
}

int main(void) {

    fnc_print(fnc_1);
    fnc_print2(fnc_2, 5);

    return 0;
}

실행 결과.

 

정말 간단하고 의미 없는 예제 입니다.

간단히 이런식으로 사용하는구나 하고 넘어가시면 되겠습니다.

 

12행과 16행을 보면 매개 변수로 함수 포인터를 전달 받는걸 확인하실 수 있습니다.

12행은 매개변수가 없고

16행은 매개변수로 int type 변수를 받고 있습니다.

 

 

 

살짝 변형해 main함수에 변수로 선언해 접근해 보겠습니다

#include<iostream>
#include<cstdio>

void fnc_1() {
    printf("fnc 함수\n");
}

void fnc_2(int n) {
    printf("%d\n", n);
}

void fnc_print(void(*fnc)()) {
    fnc();
}

void fnc_print2(void(*fnc)(int), int n) {
    fnc(n);
}

int main(void) {

    void(*fp_1)();
    void(*fp_2)(int);

    fnc_print(fp_1);
    fnc_print2(fp_2, 5);

    return 0;
}

 

22행과 23행을 보면 변수로 선언했습니다.(fp_1과 fp_2가 변수 명입니다.)

이렇게 변수로 선언해 직접 전달하는 것도 가능하고 

 

typedef를 통해 구조체나 클래스를 선언하듯이 선언할 수도 있습니다.

#include<iostream>
#include<cstdio>

typedef void(*Fn_1)();
typedef void(*Fn_2)(int);

void fnc_1() {
    printf("fnc 함수\n");
}

void fnc_2(int n) {
    printf("%d\n", n);
}

void fnc_print(Fn_1 fnc) {
    fnc();
}

void fnc_print2(Fn_2 fnc, int n) {
    fnc(n);
}

int main(void) {

    Fn_1 fp_1;
    Fn_2 fp_2;

    fnc_print(fp_1);
    fnc_print2(fp_2, 5);

    return 0;
}
 

 

위 예제들과 바뀐 곳은 3,4행에 typedef문이 추가 되었고,

15행과 19행의 매개 변수란의 형식이 바뀌었고

25, 26행에 함수 포인터 선언이 추가되었습니다.

 

 

함수 포인터는 이런식으로 사용하시면 되겠습니다.

 

 

 

그렇다면!!....

 

왜 함수 포인터를 사용할까??

 

 

가장큰 이유는 callback 입니다.

 

callback이란?

함수가 함수를 부른다는 뜻입니다.

 

잉?? 이개 무슨 소리냐?!!!

 

예시를 하나 보여드리겠습니다.

 

아래는 일반 함수로 만든 간단한 오름차순 내림차순 정렬 예시입니다.

 

#include<iostream>

#define UP 1
#define DOWN 2
using namespace std;

void simple_sort(int *arr, int n, int cmp) {
    for (int i = 0; i < n - 1; i++) {
        for (int j = i + 1; j < 5; j++) {
            if (cmp == UP) {
                if (arr[i] > arr[j])
                    arr[i] ^= arr[j] ^= arr[i] ^= arr[j];
            }
            else if (cmp == DOWN) {
                if (arr[i] < arr[j])
                    arr[i] ^= arr[j] ^= arr[i] ^= arr[j];
            }
        }
    }
}

void sort_print(int *arr, int n) {
    for (int i = 0; i < 5; i++)
        cout << arr[i] << " ";
    cout << endl;
}

int main(void) {
    int arr[5] = { 10, 5, 41, 100, 2 };

    simple_sort(arr, 5, DOWN);
    sort_print(arr, 5);

    simple_sort(arr, 5, UP);
    sort_print(arr, 5);


    return 0;
}
 

 

 

실행 결과.

 

 

단순한 내림차순 정렬과 오름차순 정렬 코드입니다.

(코드 보기 불편하시거나 귀찮으신분은 아래 글만 읽으셔도 될 것 같습니다.)

 

31행과 34행에서 UP 또는 DOWN을 인자로 받아 오름차순인지 내림차순인지 결정을 하고 있습니다.

그리고 10행과 14행을 보시면 if문 2개를 통해 결과를 처리하고 있는걸 알 수 있습니다.

 

이것이 아니라면 내림차순 함수와 오름차순 함수를 각각 정의하는 방법도 있을 것입니다.

하지만 저것은 단점이 존재 합니다.

 

11,12 행과 15,16행을 보면 코드가 매우 비슷합니다. (연산자 <, >만 다를뿐 동일합니다.)

뿐만 아니라 저렇게 정의되어 있어서 저걸 가져다 쓰려고 한다면,

프로그래머는 저 형식 그대로 써야 합니다.

즉, 코드의 유연성(효율성)이 떨어지게 됩니다.

또한 코드의 길이가 길어지게 됩니다.

 

이 말이 무엇이냐??

 

 

 

아래 예시는 함수 포인터를 이용한 예시인데 비교해 보시기 바랍니다.

#include<iostream>

using namespace std;

typedef bool(*Cmp)(int, int);
bool Up(int x, int y) { return (x > y ? true : false); }
bool Down(int x, int y) { return (x < y ? true : false); }

void simple_sort(int *arr, int n, Cmp cmp) {
    for (int i = 0; i < n - 1; i++) {
        for (int j = i + 1; j < 5; j++) {
            if (cmp(arr[i], arr[j])) {
                arr[i] ^= arr[j] ^= arr[i] ^= arr[j];
            }
        }
    }
}

void sort_print(int *arr, int n) {
    for (int i = 0; i < 5; i++)
        cout << arr[i] << " ";
    cout << endl;
}

int main(void) {
    int arr[5] = { 10, 5, 41, 100, 2 };

    simple_sort(arr, 5, Down);
    sort_print(arr, 5);

    simple_sort(arr, 5, Up);
    sort_print(arr, 5);


    return 0;
}
 

 

 

함수 포인터를 인자로 받아 간단하게 정렬를 하고 있습니다.

(코드를 보시면 이해가 안가실 것 같아서 말씀 드립니다. 모르는 부분 댓글 남겨주시면 바로 대답해 드릴게요~)

 

 

이렇게 함수 포인터로 인자를 받게 된다면 사용자가 직접 함수를 정의해 좀 더 유연성을 부여할 수 있고

코드의 길이도 짧아지게 됩니다.

 

하지만 단점도 존재 하는데,

선언이 생소할 뿐만 아니라 잘못 사용하면 코드의 가독성도 떨어지게 되므로 잘 사용해야 합니다.

 

그리고 함수 포인터를 사용할 경우 C++의 장점인 inline화 코드가 안되게 되는데

이는 함수 객체 글을 참고해 주세요!!

 

함수 객체 보러가기(클릭)

 

 

 

 

 

 

반응형

'프로그래밍 > C++' 카테고리의 다른 글

C++ vector사용법 및 설명 (장&단점)  (9) 2019.11.24
C++ for_each문 개념과 사용법 설명  (0) 2019.08.01
c++ 함수 객채란??  (0) 2019.07.18
C++ inline 함수란?  (3) 2019.07.13
C++ Lambda 사용 이유와 사용 방법  (12) 2019.07.01

댓글


스킨편집 -> html 편집에서