1절. 함수포인터란 ?

C 에서의 함수포인터는 언어와 관련된 문법적인 내용임으로 함수포인터에 대한 설명은 지극히 의례적인 내용이 될수 있겠지만, 꽤 복잡하기도 하고 재미있게 사용할수도 있으므로 굳이 강좌를 만들었다.

함수 포인터는 말그대로 함수의 위치를 가리키는 포인터이다.

C 언어의 경우 함수자체를 변수로 만들수는 없다. 대신 함수를 포인트하는 것은 가능한데, 이것을 통해서 함수를 포인터 처럼 사용할수 있으며, 이 포인터가 가르키고 있는 곳의 함수를 실행시킬수도 있다.


1.1절. 선언방법

포인터는 하나의 자료형이므로 포인터가 가르키는 데이타의 타입정보를 이용해서 포인터를 선언해줘야 한다. 물론 함수포인터의 경우 포인터하는 대상이 데이타가 아니고 함수라는 점이 다르긴 하지만.. 말이다.

다음은 함수포인터를 선언하기 위한 전형적인 방법이다.

return_type (*function)(arg1, arg2, ...);
			
이해하기 쉽게, 만약 int hello(char *) 라는 함수를 가리키는 함수포인터를 선언하고자 한다면 아래와 같이 하면 된다.
int (*func_name)(char *);
			
다음은 간단한 예제이다.

funcpter.c

#include <stdio.h>
void hello(char *name)
{
    printf ("Hi %s\n", name);
}

int main()
{
    void (*Func)(char *);
    Func = hello;
    Func("test");
}
			
위 코드를 이해하는 데에는 별 무리가 없을것이다. void 리턴타입을 가지고 인자로 캐릭터 포인터를 가지는 함수포인터를 사용하고 있음을 알수 있다.


1.2절. 왜 함수포인터를 사용하는가

익히 경험해서 알고 있겠지만, C 에서의 포인터는 잘못 사용하면 악몽이 될수 있다. 특히 함수포인터를 선언해서 쓸 경우는 그 독특한 문법과 익숙치않은 포인터개념의 짬뽕으로 인하여 코드자체가 암호문처럼 변할수도 있다. 이것은 프로그램의 유지/보수를 어렵게 할수도 있다.

그러나 그럼에도 불구하고 제대로 사용할경우 그 장점을 활용하여 유용한 프로그래밍 기법으로 활용할수 있는데, 바로 generic한 함수(혹은 알고리즘)의 작성을 가능하게 한다는 점이다. 또한 잘만 사용하면 오히려 유지/보수가 수월하게끔 만들수도 있다. 다음장에서는 간단한 예제를 이용해서 함수포인터 의 장점을 활용하는 방법에 대해서 알아보도록 하겠다.


2절. 함수포인터의 활용

이번장에서는 함수포인터의 활용방법에 대해서 몇가지 예를 들어서 알아보도록 하겠다.


2.1절. Generic 함수(알고리즘)의 작성

각 학생의 과목별 성적데이타가 있고, 과목별 최고 점수를 가져오는 프로그램이 있다고 가정하자. 처음에 이 프로그램은 각 과목중 "국어" 최고 점수만을 가져오도록 만들었다. 그런데 "수학" 최고 점수만을 가져오도록 변경하고 싶으면 어떻게 해야 할까? 혹은 최고 평점을 가져오기를 원할수도 있을것이다. 물론 각각의 경우에 대해서 별도의 이름을 가지는 함수를 만들면 되기는 하겠지만 이러한 경우에 함수포인터를 이용하면, 좀더 유지보수가 쉬운 깔끔한 코드를 만들어 낼수가 있다.

max.cc

#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <vector>

using namespace std;

typedef struct _pinfo
{
    char name[12];  
    int  math;
    int  korean;
    int  eng;
} pinfo;

void printmax(vector<pinfo> va, void (*SortFunc)(vector<pinfo>))
{
    printf("최고 성적 출력 프로그램\n");
    SortFunc(va);
}   

void engmax(vector<pinfo> va)
{
    int max = 0;
    int index = 0;
    for (int i = 0; i < va.size(); i++)
    {
        if (va[i].eng > max)
        {
            max = va[i].eng;
            index = i;
        }
    }
    printf("영어 최고점수 획득자는 %s : %d\n", 
        va[index].name,
        va[index].eng);
};


int main()
{
    pinfo myinfo;
    vector<pinfo> va;

    void (*Sort)(vector<pinfo>);

    myinfo.korean = 80;
    myinfo.eng    = 65;
    myinfo.math   = 99;
    strncpy(myinfo.name, "yundream", 12);
    va.push_back(myinfo);

    myinfo.korean = 90;
    myinfo.eng    = 65;
    myinfo.math   = 74;
    strncpy(myinfo.name, "kknd", 12);
    va.push_back(myinfo);

    myinfo.korean = 63;
    myinfo.eng    = 88;
    myinfo.math   = 55;
    strncpy(myinfo.name, "junny", 12);
    va.push_back(myinfo);

    printmax(va, engmax);
}
			
위의 코드를 보면 printmax 라는 함수가 있는데, 함수포인터를 인자로 넘겨 받음으로써, 좀더 제너릭하게 확장시킬수 있도록 만들어져 있다. 위의 경우는 영어최고점자의 정보를 출력시키도록 해놓았는데, 만약 최고 평점자에 대한 정보를 출력 시키기를 원한다면, 다음과 같은 함수를 만들고, printmax 에 인자로 넘기면 될것이다.
void avgmax(vector<pinfo> va)
{
    int max   = 0;
    int total;
    int index = 0;
    for (int i = 0; i < va.size(); i++)
    {
        total = (va[i].eng + va[i].math + va[i].korean);
        if ( total > max)
        {
            max = total;
            index = i;
        }
    }
    printf("최고평자 점정보 : \n");
    printf("이름 : %s\n", va[index].name);
    printf("영어 : %d\n", va[index].eng);
    printf("국어 : %d\n", va[index].korean);
    printf("수학 : %d\n", va[index].math);
    printf("평점 : %.2f\n", (float)max/3.);
}
			
아래와 같이 printmax 를 호출하면 된다.
printmax(va, avgmax);
			
이처럼 함수포인터를 직접 넘김으로 인해서, 프로그래머는 필요에 따라 원 쏘쓰의 큰 변화없이 자기가 필요로 하는 코드만 만들어서 쉽게 확장시킬수 있다.


2.2절. 그외의 활용

여기에서는 이러한 용도로도 사용이 가능하다는걸 보여주는 팁수준의 활용용도를 보여줄것이다.

필자가 쓰레드 프로그래밍을 할때 가끔 사용하는 방법인데, 코드를 훨씬 깔끔하고 보기 쉽게 만들어준다. 포인트는 쓰레드 함수를 함수포인터를 값으로 하는 vector 에 등록시켜서 쓰레드 생성등에 사용하는 방법이다.

이방법을 쓰면 비록 몇줄이긴 하지만 분명히 코딩량을 줄일수 있다. 그리고 쓰레드 함수를 한영역에서 모아서 관리함으로 코드를 좀더 보기 쉽게 만들어준다.

funcpt_tip.cc

#include <pthread.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <vector>
#include <iostream>

#define MAX_THREAD_NUM 20

using namespace std;

void *func1(void *)
{
    printf("Thread 1\n");
    pause();
}
void *func2(void *)
{
    printf("Thread 2\n");
    pause();
}
void *func3(void *)
{
    printf("Thread 3\n");
    pause();
}
void *func4(void *)
{
    printf("Thread 4\n");
    pause();
}

int main()
{
    // 인자가 함수포인터인 vector 생성
    vector<void *(*)(void *)> thread_list;
    vector<pthread_t> tident(MAX_THREAD_NUM);
    int status;

    thread_list.push_back(func1);
    thread_list.push_back(func2);
    thread_list.push_back(func3);
    thread_list.push_back(func4);

    cout << "등록된 쓰레드 " << thread_list.size() << endl;
    for (int i = 0; i < thread_list.size(); i++)
    {
        pthread_create(&tident[i], NULL, thread_list[i], (void *)NULL);
    }

    cout << "thread Join Wait" << endl;
    for (int i = 0; i < tident.size(); i++)
    {
        pthread_join(tident[i], (void **)&status);
    }
    return 1;
}
			


3절. 결론

이상 간단하게 함수포인터의 사용방법에 대해서 알아보았다. 함수포인터는 몇몇경우에 유용하게 사용할수 있지만, 잘못사용하게 될경우 대단히 유지보수가 어려운 코드를 만들어 낼수도 있다. 위의 함수포인터의 선언을 보면 알겠지만, 결코 익숙한 선언및 사용방법이 아니기 때문이다. 또한 포인터라는 개념자체가 혼동을 줄수 있기 때문이다.

어쨋든 사용방법은 알아두는게 좋다. 위에서는 예로 들지 않고 있지만, C 에서 객체지향을 흉내내기 위한 목적으로써, 함수포인터를 사용하는 경우도 많기 때문이다.


출처 : http://www.joinc.co.kr/modules.php?name=News&file=article&sid=119

출처 : Tong - ♡Hopi♡~님의 ♡ C/C++ ♡통

'C/C++언어' 카테고리의 다른 글

printf 문을 만들어보자  (0) 2008.01.28
여러 C++ 컴파일러  (0) 2007.09.06
[본문스크랩] 파일 입.출력  (0) 2007.09.06
[본문스크랩] VC++ Article  (0) 2007.09.06
[강좌] MASM 6.0 사용법  (0) 2007.09.06

출처: http://blog.daum.net/wooyoung

www.debuglab.com 에서 있는 자료들입니다.


이 자료를 정리하신 분은 서우석이라는 분인데..정말 대단하다라는 생각이 듭니다.

어쩜 이렇게 정리를 잘 하셨을까.. 부럽다는 생각만 드는군요..ㅡㅡ



-------------------------------------------------------------------

Home


lastest update : 2001.08.06




'C/C++언어 > 후킹' 카테고리의 다른 글

Hooking 을 사용하는 프로그램 내의 구현  (0) 2007.11.11
Global Hooking in Win32  (0) 2007.11.11
[본문스크랩] 메세지 후킹  (0) 2007.09.06
API Hooking Revealed  (0) 2007.09.06
동적 API 후킹의 구조  (0) 2007.09.06

//////////////// 파일의 모든 내용 읽고 출력 //////////////// p161
//cout를 위해, ifstream을 위해, vector를 위해, string을 위해, for_each를 위해 각각 해더 추가

#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <algorithm>

//for_each에서 string출력에 사용할 단항 함수 정의
void print_str(const std::string &str)
{
 std::cout << str << std::endl;
}

...

using namespace std;

ifstream file("test.txt");
if(!file)
{
 cerr << "Can't open file " << enld;
 exit(0);
}

// file로부터 문자열을 읽으면서 vector에 저장
vector<string> strlist;
string temp;
while(file >> temp)
{
 strlist.pushback(temp);
}
file.close();

// vector에 담긴 모든 string을 출력
for_each(strlist.begin(), strlist.end(), print_str);


//////////////// 파일 복사 //////////////// p140
#include <iostream>
#include <fstream>
#include <string>

int main(int argc, char *argv[])
{
 using namespace std;

 if(argc != 3) return 1;

 string file1(argv[1]), file2(argv[2]);
 ifstream in(file1.c_str(), ios_base::binary); // 복사할 파일 바이너리로 읽고
 ofstream out(file2.c_str(), iso_base::binary); // 복사본 파일 바이너리로 쓸준비

 out << in.rdbuf();  // 여기서 복사 진행

 return 0;
}

//////////////// 조건이 맞으면 출력 //////////////// p141
#include <iostream>
#include <fstream>
#include <string>

template<typename t>
void if_cond_is_true_write_rst(
 std::string filename,
 std::streampos cond_pos, t cond,
 std::streampos target_pos, t rst)
{
 using namespace std;
 
 // 읽기 쓰기 가능하게 파일을 연다
 fstream file(filename.c_str(), ios_base::in | ios_base::out);

 // 지정된 위치로 cond_pos 이동
 file.seekg(cond_pos);

 t real;
 file.read(&real, sizeof real);

 if(real == cond)
 {
  file.seekg(target_pos);
  file.write(&rst, sizeof rst);
 }
}
... 중략 (main 함수 등)
if_cond_is_true_rst("cond.txt", 10, 'J', 0, '-');

//////////////// 파일 크기 출력 //////////////// p144
#include <iostream>
#include <fstream>
#include <iterator>
#include <limits>

...
using namespace std;
string filename;
...

// 파일을 열고 맨 끝으로 옮긴다. (ate는 파일을 열자마다 EOF로 가게 한다)
ifstream in1(filename.c_str(), ios_base::ate);
streampos end = in1.tellg();  // EOF인 현재 위치를 받아온다
cout << "Size : " << end << endl; // 출력
in1.close();

ifstream in2(filename.c_str(), ios_base::binary);
// 파일 크기 계산
streamsize size = distance(istreambuf_iterator<char>(in2),
 istreambuf_iterator<char>());
cout << "Size : " << size << endl;
in2.close();

//////////////// 정렬 문제 //////////////// p149
//영수증 출력 문제

#include <iostream>
#include <iomainp>
#include <string>

using namespace std;

struct item{ char name[32]; int price; int num;};

void print_title(
 const string &kind, const string &price, const string &number)
{
 cout << setw(15) << kind << setw(10) << price <<setw(10) << number << endl;
}

void print_item(const item & item)
{
 cout << setw(15) << item.name << setw(8) << item.price << "원"
  << setw(8) << item.num << "개" << endl;
}

void print_total(int price)
{
 cout << setw(33) << price << "원" << endl;
}

...
item itmes[3] = {
 {"가나초콜릿", 900, 1}, { "파워에이드", 900, 1}, {"감자깡",450,2}
};

print_title("종류", "가격", "수량");
int price(0);
for(size_t i(0); i < sizeof items / sizeof (item); i++)
{
 print_item(items[i]);
 price += items[i].num * items[i].price;
}
print_total(price);


'C/C++언어 > STL' 카테고리의 다른 글

STL std::string에서 쓸 수 있는 Replace All 함수  (0) 2010.07.04
STL 컨테이너(map, multimap)  (0) 2010.07.04
STL 숙제.. 렌터카 2번째..  (0) 2008.05.02
STL 강좌입니다.  (0) 2007.09.06

// 파일 사용시 공유 DLL 사용을 체크해야 함

// API의 windows.h 와 같은거
#include <afxwin.h>

// WinMain 부분 음... 여기에 메시지 루프가 들어가 있음
class CHelloApp : public CWinApp
{
public:
 virtual BOOL InitInstance();
};

// 이부분이 API의 WndProc 부분, 메시지를 Catch 해서 처리해줌
class CMainFrame : public CFrameWnd
{
public:
 CMainFrame();

protected:
 afx_msg void OnPaint();
 afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
 DECLARE_MESSAGE_MAP()
};

// 프로그램 객체
CHelloApp theApp;

// 클래스 정의
// 이부분은 API의 CreateWindow와 같은 역활을 해줌
BOOL CHelloApp::InitInstance()
{
 m_pMainWnd = new CMainFrame;   // 객체 만들어 지면서 생성자 실행
 m_pMainWnd ->ShowWindow(m_nCmdShow); // ShowWindow 명령 실행
 m_pMainWnd ->UpdateWindow();   // UodateWindow 명령 실행
 return TRUE;
}

// 각 함수의 플러그를 보고 싶으면
// 함수에 커서를 대고 F12키를 눌러보자! 그함수를 정의한 헤더 파일등을 볼수 있다

CMainFrame::CMainFrame()
{
 Create(NULL,"HelloMFC Application");
}

// WM_PAINT 할때의 명령
void CMainFrame::OnPaint()
{
 char *msg = "Hello, MFC";
 CPaintDC dc(this);

 dc.TextOut(100,100,msg,lstrlen(msg));
}

// WM_LBUTTONDOWN 의 명령
void CMainFrame::OnLButtonDown(UINT nFlags, CPoint point)
{
 MessageBox("마우스클릭했지?","마우스",MB_YESNOCANCEL | MB_ICONQUESTION);
}

// 메시지 맵
// 이 메시지 맵은 CMainFrame의 소속이다
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
// MFC에이미 정의가 되어있는 키워드
 ON_WM_PAINT()    
 ON_WM_LBUTTONDOWN()
END_MESSAGE_MAP()
// 맨 마지막 줄은 DefWnidowProc 와 같은 명령을 해준다

// 이 다음에 WinMain 함수가 생략되어 있음.
// 이 소스에서 실행하는건 프로그램 객체 선언 CHelloApp theApp; 이 한줄 뿐임.
// WinMain 함수는 어느 윈도우나 같으므로 아예 생략되었음. (컴파일할때 자동 처리)
// WinMain 함수를 보고 싶으면 BOOL CHelloApp::InitInstance() 줄에 브레이크 포인트를 걸어볼껏
// .net 에서만 유효, 호출 스택을 잘 보면 나옴

+ Recent posts