도저히 모르겠습니다. 함수포인터 c 언어

mui의 이미지

:wink:

홍릉과학출판사에서 낸 a book on c 라는 책을 보다가

포인터 부분에서 함수 포인터라는 것을 공부하고 있는데,

함수 포인터의 효용성이 무엇인지, 차라리 그냥 함수를 호출하면되지

왜 함수포인터를 주어서 함수를 호출하는지 이해가 안갑니다.

메모리 절약 측면이 있는것인지요??

고수님들의 조언을 부탁드립니다.

감사 ^^

pynoos의 이미지

같은 데이터를 가지고 다른 일을 하도록 설정하는 일에 함수 포인터를 씁니다.

대개 callback 함수 형태로 어떤 일이 발생했을 때, 사용자 정의 행동을 하게 만들고 싶을 때 쓰지요.

예를 들면, unix의 signal handler 등록이 있을 수 있는데, 이것은
kernel에서 process에게 signal을 전달할 때, 불려지는 함수입니다.
또, thread 를 생성한 다음 생성된 thread에 시작 함수를 전달할 때도
사실은 thread creation함수의 callback이지요.
window 계열에서 nonblock i/o 에서 데이터 전송이 끝났을경우에
호출될 함수 등록하는 경우도 있고.. 사용되는 곳은 무척 많습니다.

C++ 의 runtime function overriding 에 쓰이는 virutual function도
내부적으로는 함수 포인터를 사용합니다.
잘 생각해보면, 이것도 미리정의된 상위 클래스에서 runtime 시에 등록되는
pointer에 따라 하위 클래스의 멤버함수 pointer를 부르는 방법으로 작동
합니다.

즉, 인터페이스를 표준화하고, 사용하는 사람이 그 인터페이스를 따라가게
만들고자하는 모든 곳에 주로 함수 포인터가 사용됩니다.

choissi의 이미지

qsort나 bsearch 같은 상황에도.. 필요하겠죠

       void qsort(void *base, size_t nmemb, size_t size,
              int (*compar)(const void *, const void *))

       void *bsearch(const void *key, const void *base, size_t nmemb,
              size_t size, int (*compar)(const void *, const void *));

울랄라~ 호기심 천국~!!
http://www.ezdoum.com

logout의 이미지

쉽게 생각하세요. 포인터는 전체적으로 정적인 자료 구조와 동적인 자료 구조의 차이를 정확히 이해하셔야 합니다. 그리고 컴파일 타임과 런타임의 차이점도 이해하셔야 하구요. 둘 다 똑같은 얘기입니다. 한번 예를 들어 보죠. 우선 씨에서

    int foobar[4];

    int* foobar;

    foobar = malloc(4 * sizeof(int));

는 엄연한 차이가 있습니다. 둘 다 똑같이 정수형의 변수 4개를 선언하지만 그 내부 구조는 완전히 다릅니다. 코드를 좀 더 바꾸어 보면...

    int n;
    scanf("%d", n);

    int foobar[n];

이 코드는 컴파일 하면 에러가 납니다. 가장 최근의 씨는 이것도 컴파일이 된다고 합니다만 어쨌든 개념 이해를 해 보십시다. 컴파일러의 입장에서 int foobar[n]; 이라는 선언문은 정말 황당한 선언문입니다. foobar 배열을 만드는 건 좋은데... 도데체 n이 얼마가 될 지 알 수 없는 노릇이죠. 사용자가 입력하는 n의 값에 따라 n이 1이 될수도 1000, 10000, 100000이 될 수도 있는 노릇이니까요. 아니 이거 돌려보면 가능하지 않느냐.... 맞는 소립니다. 그러나 컴파일러는 실행 바이너리를 돌리기 전에 만들어야 합니다. 직접 실행해봐야 아는 정보를 돌리기 전에 처리하라고 했으니 당연히 컴파일러가 에러를 내는 것이죠.

이것을 malloc을 이용해서 동적(dynamic)하게 바꾸면 이렇게 됩니다.

    int *foobar;
    int n;

    scanf("%d", n);

    foobar = (int*) malloc(n * sizeof(int));

malloc은 이름 그대로 memory allocation을 해 줍니다. 들어온 파라메터의 크기만큼 메모리를 만들고 그 메모리의 첫머리 주소를 리턴해 줍니다. (int*) 타입 캐스팅은 이 메모리 덩어리가 int 라는 요소로 하나하나 구성되어 있다고 알려 주는 것입니다.

이 코드는 앞의 코드와 뭐가 다를까요? 이제 컴파일러는 이 코드를 컴파일하는데 문제가 없습니다. n 값을 받아서 malloc() 함수로 넘겨주는 실행 바이너리를 생성하면 그것으로 끝입니다. 그렇다면 malloc() 함수의 구현은요? 일단은 신경쓰지 맙시다. 이 함수의 구현은 표준 라이브러리에 재주껏 구현이 되어 있습니다. 어쨌든, 이런 식으로 메모리를 동적으로 사용하는 길이 포인터를 이용, 가능하게 되었습니다.

어떤 학교의 전체 학생 데이터베이스를 한번 간단히 프로그램으로 작성한다고 생각해 봅시다. 이 학교의 전체 학생수는 2000명 이라고 한다면 학생들의 이름은 간단히 String student_name[2000]; 과 같은 선언문으로 처리할 수 있을 겁니다. 그런데 이 프로그램을 100명 정원의 학교, 1000명 정원의 학교, 10000명 정원의 학교에도 적용한다고 생각해 봅시다. 만명 정원의 학교에 이 프로그램을 쓰려면 String student_name[10000];을 선언하고 전체 프로그램을 재컴파일 해야 합니다. 그런데 malloc()을 써서 dynamic 하게 메모리 할당을 하는 경우를 생각해 보세요. 프로그램 하나로 어떤 경우에나 대처가 가능합니다. 이래서 자꾸만 프로그래밍 언어들이 dynamic한 쪽 특성을 강조해 나가는 겁니다. 씨는 포인터로 이것을 구현하고 있고, 기타 객체지향 언어들은 조금 다른 모습을 가지고 있습니다. C++이나 자바에서 new 라는 오퍼레이터를 보시면 C의 malloc() 을 떠올리시면 됩니다. 이해하기는 new 오퍼레이터가 쉽습니다만.

이제 함수 포인터를 dynamic의 관점에서 생각해봅시다. 음... 함수 포인터는 저도 약해서... 대강 말로 하겠습니다. :)

보통 유저 인터페이스를 프로그래밍하다보면 callback 펑션이라는 개념을 씁니다. 그러니까... 예를들어 ok 버튼을 누르면 ok 버튼과 연결된 함수가 호출되는 개념입니다. ok 버튼과 foobar() 함수를 연결시키려면 당연히 foobar() 함수가 선언되어 있어야 겠죠.

그런데 가끔씩은 이 ok 버튼과 foobar() 함수의 연결을 프로그램 실행 도중에 끊어버리고 다른 foobar2() 함수와 연결시켜야 할 경우가 있습니다. 간단히 예를 들면, "전송"버튼을 눌렀을때 사용자의 컴퓨터가 네트웍에 연결되어 있는 경우는 send_data_over_the_network() 함수를 호출하고, 사용자의 컴퓨터가 네트웍에 연결되어 있지 않는 경우는 display_error_message() 함수를 호출한다고 생각해 봅시다. 전송 버튼에 s_d_o_t_n() 함수를 연결시킬지 아니면 d_e_m() 함수를 연결시킬지는 실제 컴파일된 바이너리를 실행해 보기 전에는 알 수 없습니다. 이런 경우에 함수 포인터를 쓰면 상황에 따라 필요한 함수를 버튼에 연결해 쓸 수 있는 것이죠. 런타임시에 말입니다.

물론 이정도의 간단한 기능은 if else 로 구현이 가능합니다. 그러나 if 를 이용해 구현하면 아까 String students[1000]; 의 경우가 또 발생합니다. 이런 까닭에 함수 포인터라는 것을 씁니다.

쓰고 보니 함수 포인터 쪽 설명은 제가봐도 성이 좀 안차네요. 어쨌든 C++의 virtual function 이나 기타 객체지향 언어의 dynamic binding이 함수 포인터와 맥락이 통합니다. 일단 함수 포인터는 메모리의 동적 할당과 관련이 깊다는 사실을 머릿속에 넣어두세요.

"I conduct to live,
I live to compose."
--- Gustav Mahler

heoks의 이미지

변수라는 것은 어떤 동일한 type의 값을 저장하기 위한 것입니다.

프로그램 코드에서 매번 실행해 주어야 할 함수들이 틀려지는 경우
함수포인터로 관리를 하는 것이 효율적입니다.

운영체제의 많은 구조체들이 함수포인터를 포함하고 있습니다.

예를 들자면

typedef struct List_{
int size;
int (*match) (const void *key1, const void *key2);
void (*destroy) (void *data);
ListElmt *head;
ListElmt *tail;
} List;

위의 구조체는 O'REILLY에서 나온 Mastering Algorithms with C의
LinkedList의 구조체 입니다.
여기서 match와 destroy라는 함수 포인터가 나오는데
자신이 어떤 작업(어떤 데이터를 다루느냐)을 하느냐에 따라서 match와 destroy에 다른 함수를 연결 시켜 줄 수 있는 것입니다.

코드를 가능한 고치지 않고 다른 기능을 할 수 있게 하는 것이 프로그램을 작성하는 커다란 재미(?)가 아닌가 싶습니다. C에서는 포인터를 잘 다루면 그런일을 할 수 있습니다.

ihavnoid의 이미지

    int n;
    scanf("%d", n);

    int foobar[n];

    int *foobar;
    int n;

    scanf("%d", n);

    foobar = (int*) malloc(n * sizeof(int));

근데요... 이거 둘다 어차피 에러 나지 않나요?
scanf("%d", n); -> scanf("%d", &n);
(설마 최근의 c에서는 이런거 지원되는 거 아니겠죠??)

아참... 사용 용도를 잘 이해하지 못하신다면... 직접 사용하는 코드를 살펴보시는 것도 좋을 듯 합니다... function pointer는 여러모로 유용합니다...

음...지금 당장 생각나는 건 device driver 정도군요....
직접 character device를 만들 경우, 이 device는 read는 이 함수, write는 저 함수, seek는 요 함수를 이용하면 된다... 식으로 driver 입장에서 초기화를 해 줘야 합니다... 이때 function의 포인터를 돌려줄 경우 매우 간편하죠...

정확히 이런 형태는 아니지만.... 대충 psuedo-code로 하자면... 드라이버를 초기화 해 주는 루틴에서...

int init_driver(driver_struct * driv)
{
    driv->open_function = my_open;
    driv->read_runction = my_read;
/*      등등..    */
    return SUCCESS;
}

이런 식으로 해 줍니다...

나중에 커널에서 이를 이용할때는.... 이때 채워넣은 driv struct 를 이용해서...

    the_driver[node_num]->open_function(filename, mode, permission, &result);

뭐 이런식으로 합니다....

결국 driver 입장에서는 struct 하나를 돌려줌으로 자기 자신의 정보를 몽땅 돌려줄 수 있겠고요, 커널 입장에서는 driver가 채워넣은 함수를 그냥 호출하면 되겠죠.. 굳이 복잡한 switch/case 문 같은 거 안 쓰고요....

직접 상황에 직면하지 않으면 이해가 힘드실 수도 있습니다...

이 이외에도 사용 용도는 무궁무진합니다..

Consider the ravens: for they neither sow nor reap; which neither have storehouse nor barn; and God feedeth them: how much more are ye better than the fowls?
Luke 12:24

logout의 이미지

틀린 것 맞습니다. scanf("%d", &n); 이네요. 요즘 프로그래밍을 손놓고 있다보니 이런 기초적인 것도 실수를 하네요. :)

처음 포인터를 배우면서 scanf("%d", &n);의 n앞에 왜 &가 붙어야 하는지 문득 깨닫고 무척이나 즐거웠던 기억이 납니다.

"I conduct to live,
I live to compose."
--- Gustav Mahler

ㅡ,.ㅡ;;의 이미지

함수에 인자로 함수를 넘기고 싶을때..
-- 예를 들어볼까요? 함수에 값은 많이 넘겨 봤죠? 그런데 어떠한 동작
을 하라는 동작방식도 넘겨주고 싶다면 어떻게 하죠?

그리고.. 어떤 라이브러리함수가 내가 금방만든함수를 실행하도록 하고싶으면 어떻게 하죠? 라이브러리가 내가만든 함수의 이름을 어떻게 아나요?
모르니까 포인터러 넘겨야겠죠..

그리고.. 상황에따라 함수자체를 교체해주고 싶으면 어떻게 하면될까요??
미리부터 대상함수를 모두 if else 등으로 역어두면 되기야 하겠지만..
함수명을 미리부터 알고 있어야 가능하겠죠.. 모른다면 불가능하단말이죠.

위내용셋다 비슷한내용이지만.. 프로그래밍의 구조화를 잘하기위해서 꼭필요하겠죠..


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

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.