[ 함수 포인터 ] |
함수 포인터란?
"함수의 이름은 메모리상에 존재하는 함수의 위치를 가리키는 포인터"
함수 포인터의 포인터 타입
int func( int a ) { a++; return a; } |
위의 코드를 풀어서 보면,
리턴타입이 int형이고 int형 인자 하나를 전달 받는 함수다.
함수 포인터의 포인터 타입을 결정하는것은 리턴형과 전달인자이다.
int (*fPtr1)(int);
위의 선언은 리턴타입이 int형이고 매개변수로 int형 변수 하나를 받는 함수포인터인 것이다.
[코드]
위의 코드는 함수포인터의 선언부터 호출까지 보여줍니다.
함수포인터를 왜 쓸까?
함수포인터는 운영체제에게 우리가 정의한 함수를 알려 줄 때 유용하게 사용되는 개념입니다.
우리가 정의한 함수의 이름을 운영체제에 전달하면 운영체제는 전달된 함수 이름을 이용해서 우리를 대신해 함수를 호출해줍니다. 이때 해당되는 개념이 콜백함수(CallBack Function)라고도 하죠.
뿐만아니라, 리턴타입과 매개변수의 갯수만 맞게 함수포인터를 선언하게되면,
각각 기능을 하는 함수를 여러개를 정의 하고, 선언한 함수포인터로 필요에 맞게 변경만 시켜주는 swich부분에서도 많이 쓰이곤 합니다.
순수 저의 생각과 경험으로 함수포인터를 보자면 ...
일단 예를 들어 봅시다. 글보단 코드를 짜서 설명하는게 아무래도 쉽겠죠;;;
위의 코드는 아주 간단하게 함수포인터를 이용해서 구현한 버튼 처리입니다.
이 코드는 함수포인터를 활용해서 코드가 얼마나 간결해지고, 가독성이 높은지 설명하는 소스이므로, 많은 기능은 빠져 있습니다.
간략하게 소스를 보자면,
우선 GUI_BUTTON 구조체를 보면, 선택인덱스와 함수포인터(CALLBACK함수)가 있습니다.
그리고 GUI_BUTTON 구조체를 이용해 배열을 하나 만들었습니다. 또한 우리가 버튼을 선택했을때 CALLBACK 되는, 즉! 호출되는 함수도 funcBtn1, funcBtn2로 정의 하였습니다.
main() 함수에선 등록되어있는 버튼을 하나하나 화면상에 보여주고, 등록되어있는 버튼중에 인덱스를 입력하게 되면, 함수포인터로가 가르키는 함수를 호출하게 되는 구조입니다.
일단 알겠는데,
꼭 저렇게 함수포인터를 사용해서 코딩을 해야하나? 아직까지도 확 FEEL이 오지는 않습니다.
하지만,
코드를 자세히보면 우리는 main()을 건드릴(?) 필요가 없습니다.
버튼생성을 하고, 버튼이 선택되었을때 처리되는 함수(ex. funcBtn3)만들어서 연결(?)만 시켜주면, main() 함수에서 이루어지는 로직을 그대로 사용할 수 있는것이죠.
main()함수에서는 버튼을 등록시켰으니 화면상에 등록되어진 버튼을 보여줄테고, 그 버튼을 선택하면 우리가 새로정의해놓은 함수가 실행되겠죠.
위의 코드는 간단하게 구현되어서 그렇지 프로그램이 커지고 몇만줄의 코딩을 하면서, 모듈화 하기에는 여간 힘든게 아닙니다. 그런데 위의 예제 처럼 함수포인터를 활용하게 되면, 우리는 유지보수나 리펙토링 하기가 쉬워집니다. 가독성도 높죠.
아직도 왜 사용하는지 해깔리네요~ 확실한게 없네요( 유지보수, 리펙토링, 가독성 높아진다네 )
추상적으로만 보여지네요. 사실 이부분이 함수포인터는 가장 중요한게 아닙니다. ( 아래 CALLBACK, 동적바인딩부분이 중요함 )
버튼을 정의하고 처리하는 함수만 만들면되니 유지보수하기에도 쉬워질테고, 코드가 간결하니 가독성도 높아지고, main()함수에서 버튼을 좀더 빠르게, 호출하기위해 리펙토링을 시작했다고 치면 main()함수에서 함수를 호출하는 부분만 손을 조금 바주면 빠르게 작업이 가능하게되죠.
무엇보다 확실한 한방은? main() 함수는 메인 로직만 있고, 추가작업이나 건드리지 않고, 함수포인터 없이 이 프로그램을 구현해 보면, 함수포인터의 필요성을 느끼게 되는거죠~ 이게 가장 확실하긴 합니다. 프로그램을 만드는거야 어떻게 해서든 어렵지는 않게 만들수 있습니다. 문제는 몇백개가 되는 버튼을 관리하고 호출하기에는 코드가 간결해지기 힘듭니다. 명시적으로 잘 보이지도 않죠.
정리를 해보자면,
위의 예제프로그램에서는 오히려 우리에게 프로그램이 요청하는 꼴이 되는 것입니다.
우리가 할일은 버튼만 등록해주면 끝이니까
이 프로그램은 우리에게
"내가 리턴타입이 void형이고 매개변수가 없는 함수포인터를 만들어놨으니, 프로그래머들은 함수포인터 타입에 맞게 버튼을 정의해서 나한테 입력해줘~ 그럼 내가 그 함수 처리해줄께"
가장중요한 함수포인터의 활용 중 CALLBACK에 해당되는 사항이죠~ 흔히들 예기하는 운영체제에 등록하고, 자동으로 운영체제에서 호출되는 함수들은 모두 함수 포인터를 지녔습니다.
이 모든건 프로그램 실행 동안에 즉! Runtime 동안에 동적으로 처리되는 사항입니다.
이것을 함수의 동적바인딩(프로그램 실행 도중에 어떤함수를 실행할지 선택)이라고 하죠.
결과적으로 함수포인터는 동적바인딩을 가능하게 하기위해서 필요한 것이죠~
동적바인딩 자세한 사항은 아래 링크에서 참고
http://blog.naver.com/PostView.nhn?blogId=julyflower50&logNo=60018310034
어느정도 이해가 가셨는지 모르겠지만,
분명한건 쓰고안쓰고는 프로그래머 몫입니다. 상황에 맞게 대처 해야합니다.
함수 포인터를 가까이 하는 습관을 기르자.
typedef VOID (CALLBACK *PCALLBACKGUIEVENT) ( UINT nEvent, int nControlID );
─────────────────────────────────────────────
[ void형 포인터 ] |
void형 포인터란?
변수이건, 함수이건, 포인터주소값이던, 어떠한 뭐든 담을 수 있는 포인터이다.
void형 포인터는 주소값을 저장하는 것 이외에 아무것도 할 수 없기때문에, 명시적 형변환 과정을 무조건 거쳐야 한다.
void형 포인터는 언제, 왜쓰는가?
void형 포인터는 포인터의 타입이 결정되지 않는 상태에서 주소값을 미리 저장해놓고, 포인터 타입은 나중에 결정할때 사용한다. 주로 동적할당시 많이 사용한다. malloc함수 리턴타입이 (void*)
malloc 함수처럼 타입에 상관없이, 공통되는 분모가 생길때 void형 포인터를 선택하거나,
다양한 자료형을 저장할때 등등
'0x0001 > C, C++' 카테고리의 다른 글
[C++] const, static 클래스 멤버 (0) | 2019.02.08 |
---|---|
[C언어] 메모리 관리와 동적할당 (0) | 2019.02.08 |
[C언어] 다차원 배열, 다중 포인터 (0) | 2019.02.07 |
[C언어] const 키워드 (0) | 2019.02.07 |
[C언어] extern "C" (0) | 2019.02.07 |