함수 포인터 매개변수 - hamsu pointeo maegaebyeonsu

함수 포인터 기본 사용법과 간단한 활용 예를 알아보자. 예제는 C++, C에서도 동일하게 사용 가능하다.

1. 함수 포인터 기본 사용법

함수 포인터 선언 방법은 

반환 값 (*함수포인터이름)(매개변수 타입); 으로 이루어진다.
예) void (*m_work_handler)(int inputData);

아래 예제를 보자. 함수 포인터를 만들고 정의하기 위해서 라이브러리 개발자가 고심한 것이 눈에 보인다. 함수 앞에 그냥 *만 사용하면 void *와 헷갈리기 때문에 어쩔 수 없이 () 괄호로 묶어야 했을 것이다. 이런 고충을 생각하면서 외우면 잘 기억난다. 함수 포인터도 함수기 때문에 꼭 매개변수 타입을 지정해 주어야 한다.

//함수 포인터 선언
void (*m_work_handler)(int inputData);

2. 함수 포인터 사용 예

m_work_handler에 move_to_box함수를 집어넣고 m_work_handler를 실행하면 move_to_box 함수가 실행되는 것을 알 수 있다.

//함수 포인터 선언
void (*m_work_handler)(int inputData);
//기본 함수 선언(함수 포인터 매개변수 리턴타입과 맞추어 선언합니다.)
void move_to_box(int speed);

void main()
{
	//함수 포인터(m_work_handler)에 move_to_box 함수 집어 넣기
    m_work_handler = move_to_box;
    m_work_handler(100);
}

void move_to_box(int speed)
{
	//작업 내용
	printf("상자를 %d 속도로 옮깁니다.",speed);
}

==========out============
상자를 100 속도로 옮깁니다.

이를 좀 더 활용해 보자.

m_work_handler 함수 포인터에 함수만 바꾸어주면 동일한 함수 포인터로 다른 함수를 수행할 수 있다. 이 예제를 좀 더 활용하면 제사 용성을 높이는 코드를 개발할 수 있다.

//함수 포인터 선언
void (*m_work_handler)(int inputData);
//기본 함수 선언(함수 포인터 매개변수 리턴타입과 맞추어 선언합니다.)
void move_to_box(int speed);

void main()
{
	//함수 포인터(m_work_handler)에 move_to_box 함수 집어 넣기
    m_work_handler = move_to_box;
    m_work_handler(100);
    
    m_work_handler = throw_away_the_box;
    m_work_handler(50);
}

void move_to_box(int speed)
{
	//작업 내용
	printf("상자를 %d 속도로 옮깁니다. \n",speed);
}
void throw_away_the_box(int speed)
{
	//작업 내용
	printf("상자를 %d 속도로 버립니다. \n",speed);
}

==========out============
상자를 100 속도로 옮깁니다.
상자를 50 속도로 버립니다.

3. 함수 포인터 매개변수 및 반환  예

함수 포인터를 매개변수로 전달하거나 return 값으로 반환이 가능하다. 이를 C++ Class를 사용해서 설명해 본다

Robot class는 m_work_handler이라는 함수 포인터를 가지고 있고 이 함수 포인터를 set(설정)해주거나 get(반환)하는 함수를 가지고 있다.

class Robot
{
  private:
    void (*m_work_handler)(int inputData);
    
  public:
    //함수 포인터 매개변수 사용
    void set_work_handler(void (*inn_m_work_handler)(int inputData))
    {
    	m_work_handler = inn_m_work_handler;
    }
    //함수 포인터 리턴 사용
    void (*get_work_handler())(int inputData)
    {
        return m_work_handler;
    }
};

3.1 매개변수로 사용될 때

매개변수로 사용될 때는 기존 함수 포인터를 선언했던 것 그대로 매개변수 위치에 입력해 주면 된다.

기본 함수 선언 방법:
리턴 타입 함수이름(매개변수)

함수포인터 매개변수 선언 방법:
리턴타입 함수이름(함수포인터_반환타입 (*매개변수로 사용할 함수포인터 이름)(함수포인터_매개변수))

  public:
    void set_work_handler(void (*inn_m_work_handler)(int inputData))
    {
    	m_work_handler = inn_m_work_handler;
    }

3.2 리턴으로 사용될 때

이 경우가 조금 특이한데 기존 함수 포인터 이름 지정은 필요 없어지기 때문에 제거되고 반환 타입과 매개변수 타입만 남게 된다. 설명보다는 아래 '함수포인터 매개변수 선언 방법'을 보자.

함수포인터 매개변수 선언 방법:
함수포인터_반환타입 (*함수이름(매개변수))(함수포인터_매개변수)

  public:
    void (*get_work_handler())(int inputData)
    {
        return m_work_handler;
    }

예제를 통해 한번 따라 하고 외우거나 헷갈리면 선언 방법을 숙지해서 사용하는게 좋을 듯하다.

C언어의 함수에 관해서는 이전 포스팅에서 설명했다.

https://ansan-survivor.tistory.com/1224

[C언어] C언어 함수 만들기, 함수 구조, 함수 템플릿, 함수 작동 순서

C언어 함수의 구조는 [return 타입] 함수명( [input타입 변수명] ) ex)  int  main(  int a  ) 함수명은 말 그대로 함수 이름 지정. input 은 이 함수가 실행되기 위한 입력 값을 지정하는것. return 타입은..

ansan-survivor.tistory.com

함수 포인터 매개변수 - hamsu pointeo maegaebyeonsu

C언어 함수의 구조는 아래와 같다.

[return 타입] 함수명( [input타입 변수명] )

ex)   int   main(   int a   )

함수명은 말 그대로 함수 이름 지정.

input 은 이 함수가 실행되기 위한 입력 값을 지정하는것.

return 타입은 이 함수가 연산이되어 return되는 값의 타입.

즉 자세히 보면, 2가지의 Type이 선언되어야 한다.

1. input이 되는 값의 타입

2. return되는 인자의 타입


함수는 컴파일시 그 이름 자체로 메모리에 있는 특정 주소값으로 치환된다. 즉 함수 이름자체는 "상수형 포인터"이다.

즉, 함수의 이름 그자체는 실제로 메모리의 특정 주소값 (0x0000)를 가리키고 있는 포인터 이다.

다만 함수이름 포인터는 input인자의 type정보와, return인자의 type정보가 함께 제공되어야 한다. (void가 아니라면)

함수 포인터 매개변수 - hamsu pointeo maegaebyeonsu
함수 선언시 메모리의 특정 주소에 포인터로 가리킨다. 이때 input, return 타입정보도 함께 입력된다.

* 함수포인터를 선언하는 방법

함수 포인터 매개변수 - hamsu pointeo maegaebyeonsu

1. 함수의 이름은 함수가 저장된 메모리공간을 가리키는 상수형 포인터이다. (함수 이름 자체는 상수형 타입으로 변경 불가)
2. 함수이름의 의미하는 '주소값' 은 "함수 포인터 변수"를 선언해 저장가능. (함수의 주소값을 가리키는 변수 선언 가능)
3. 함수포인터변수를 선언하려면, 사용할 함수의 반환, 매개변수 Type(자료형)을 알아야 한다.

아래 예제에서는 2개의 함수를 선언했다.

그리고 

int (*fptr) (int, int);  <= input parameter의 이름은 중요하지 않다. type만 넣으면 된다. (2개가 입력되므로 int, int)
void (*ptrstring) (char*);

두 함수포인터를 선언해서 사용.

#include <stdio.h>

/*
	포인터를 선언하기 위해서는, 주소값과 자료형이 주어져야 하는데, 함수의 이름을 다음과 같이 생각하고 포인터를 선언한다.
	함수 읽기. (함수포인터를 선언하기 위하여)
	ex) int fct(int a );
	1.
		이 함수의 주소값은? fct의 주소. (함수이름 = 주소값)
	2.
		type형은?	"Parameter가 int 이고, return값이 int 이다."
	ex) double Fct (double a , double b );
	1.
		함수의 주소값? 함수이름 Fct 의 주소.
	2.
		type형은?	"Parameter가 2개의 double, return값이 double 이다."
        
	아래 예제에서 선언한 함수포인터변수 2개.
	int (*fptr) (int, int);
	void (*ptrstring) (char *);
*/


int fct(int a, int b)
{
	int c = 0;

	c = a - b;

	return c;
}

void ShowString(char* str)
{
	printf("%s \n", str);
}

int main()
{
	char* str = "ich liebe dich";


	// 함수포인터를 선언하기!!
	// [자료형: 매개변수 int형인, 리턴값이 int형 2개인] 포인터함수 fptr 를 선언!!
	//	함수이름은 의미없다. *만 있으면 될뿐, 그러나 자료형은 반드시 일치해야 한다.
	int(*fptr) (int, int);


	fptr = fct;		//	fptr -> fct의 주소를 가리킴. (함수이름이 주소값이므로)

	// 상수 함수 포인터로 출력
	printf("%d \n", (fct(5, 7)));

	// 변수 함수 포인터로 출력
	printf("%d \n", (fptr(10, 15)));


	// 함수 포인터 선언하기.
	// 자료형: char * (케릭터 포인터형을 매개변수로 받고, return값은 없다)
	void(*ptrstring)(char*);

	// ptrstring -> ShowString함수를 가리킴.
	ptrstring = ShowString;

	ShowString(str);
	ptrstring(str);

	return 0;
}

/*
	fct 는  '상수' 함수포인터 이름이 될 수 있다! (변경 불가)
	fptr 은 '변수' 함수포인터 이름이 될 수 있다! (변경 가능)
*/

(결과)

받는 인자 int, int 와 출력인자 int 타입인 *fptr 이름의 함수포인터를 선언.

fptr = fct 

함수 포인터 매개변수 - hamsu pointeo maegaebyeonsu

위 과정을 아래와 같이 도식화 하면,

함수 fct는 이미 특정 메모리주소에 '상수형포인터' 으로써 저장되고,

함수포인터가 선언되고 그 함수를 인자로 받게 되면, 이제 그 함수를 가리키게 된다.

함수포인터는 '변수형포인터' 으로 동일하게 사용한다.

함수 포인터 매개변수 - hamsu pointeo maegaebyeonsu

문자열도 마찬가지로 함수포인터를 선언하면 동일하게 사용 가능하다.

함수 포인터 매개변수 - hamsu pointeo maegaebyeonsu