함수의 인자로 더블 포인터를 받는 이유가 뭔가요?

superkkt의 이미지

포인터의 포인터를 더블포인터라고 부르는거 맞나요? 마땅한 호칭을 모르겠네요..

어떤 함수는 인자로 더블포인터를 받는 경우가 있습니다. 예를들어 아래와 같은 함수요..

 int getaddrinfo(const char *restrict nodename,
       const char *restrict servname,
       const struct addrinfo *restrict hints,
       struct addrinfo **restrict res);

마지막 res 인자를 더블포인터로 받는데요.. 저 함수는 실행 결과를 res가 가르키는 구조체에 넣어주는데.. 굳이 저렇게 더블포인터를 안쓰고 그냥 일반 포인터를 사용해도 충분히 같은 동작을 할 수 있지 않나요?

저런 상황에서 왜 더블포인터를 쓰는지 궁금합니다.

정태영의 이미지

res 자체를 바꾸기 위해서가 아닐까요? 만약 res = NULL; 일 경우라던지... 그런 경우도 대비하기 위한게 아닐까 싶습니다 :)

오랫동안 꿈을 그리는 사람은 그 꿈을 닮아간다...

http://mytears.org ~(~_~)~
나 한줄기 바람처럼..

정태영의 이미지

superkkt wrote:
마지막 res 인자를 더블포인터로 받는데요.. 저 함수는 실행 결과를 res가 가르키는 구조체에 넣어주는데.. 굳이 저렇게 더블포인터를 안쓰고 그냥 일반 포인터를 사용해도 충분히 같은 동작을 할 수 있지 않나요?

man 페이지를 보니 더욱 확실해지네요

       struct addrinfo {
           int     ai_flags;
           int     ai_family;
           int     ai_socktype;
           int     ai_protocol;
           size_t  ai_addrlen;
           struct sockaddr *ai_addr;
           char   *ai_canonname;
           struct addrinfo *ai_next;
       };

       getaddrinfo(3) sets res to point to a dynamically-allocated linked list
       of  addrinfo  structures, linked by the ai_next member.  There are sev-
       eral reasons why the linked list may have more than one addrinfo struc-
       ture,  including:  if  the  network host is multi-homed; or if the same
       service is available from multiple socket  protocols  (one  SOCK_STREAM
       address and another SOCK_DGRAM address, for example).

res 구조체에 결과를 넣어주는게 아니라 동적으로 할당해서 ai_next 멤버로 연결시켜주는 구조이므로... 만약 리스트가 비어있을 경우를 대비해서 pointer 의 pointer 로 받는 것입니다.

오랫동안 꿈을 그리는 사람은 그 꿈을 닮아간다...

http://mytears.org ~(~_~)~
나 한줄기 바람처럼..

superkkt의 이미지

int func(char **ptr)
{
    *ptr = malloc(1024);

이것과 비슷한 경우군요.. 그런데 이 코드를 아래처럼..

int func(char *ptr)
{
    ptr = malloc(1024);

이렇게 해도 되던데요.. 두 경우의 차이점을 확실히 모르겠습니다. 살짝 이해가 되는듯하면서도 좀 더 생각하면 복잡해지고.. 아~ 또 포인터 때문에 머리 아파지는군요.. :oops:

======================
BLOG : http://superkkt.com

정태영의 이미지

superkkt wrote:
int func(char **ptr)
{
    *ptr = malloc(1024);

이것과 비슷한 경우군요.. 그런데 이 코드를 아래처럼..

int func(char *ptr)
{
    ptr = malloc(1024);

이렇게 해도 되던데요.. 두 경우의 차이점을 확실히 모르겠습니다. 살짝 이해가 되는듯하면서도 좀 더 생각하면 복잡해지고.. 아~ 또 포인터 때문에 머리 아파지는군요.. :oops:

아래코드는 함수 밖에서 확인하면 ptr 값은 변화가 없을텐데요?

#include <stdio.h>

void func( char* ptr ){
        ptr = (char*)1;
}

int main( int argc, char** argv ){

        char* ptr = NULL;

        printf( "before call func ptr is %x\n", ptr );

        func(ptr);
        printf( "after call func ptr is %x\n", ptr );

        return 0;

}

위와 같은 코드를 컴파일해서 돌려보시기 바랍니다 :)

Quote:
unfix test # gcc test.c
unfix test # ./a.out
before call func ptr is 0
after call func ptr is 0

분명히 ptr 값은 변하지 않은 걸 확인할 수 있습니다.

오랫동안 꿈을 그리는 사람은 그 꿈을 닮아간다...

http://mytears.org ~(~_~)~
나 한줄기 바람처럼..

moonzoo의 이미지

superkkt wrote:
int func(char **ptr)
{
    *ptr = malloc(1024);

이것과 비슷한 경우군요.. 그런데 이 코드를 아래처럼..

int func(char *ptr)
{
    ptr = malloc(1024);

이렇게 해도 되던데요.. 두 경우의 차이점을 확실히 모르겠습니다. 살짝 이해가 되는듯하면서도 좀 더 생각하면 복잡해지고.. 아~ 또 포인터 때문에 머리 아파지는군요.. :oops:

2번째 소스는 함수 처음에 malloc을 바로 한다면..다음코드와
마찬가지입니다.

int func()
{
    char *ptr
    ptr = malloc(1024);

1번째 소스와는 엄청난 차이가 있죠^^.

superkkt의 이미지

이제 좀 이해가 되었습니다. 이것도 역시 결국엔 call by value와 call by reference의 차이점인듯 싶네요.. 포인터 변수 자체를 바꾸고 싶으면 pointer의 pointer로 넘겨야 하는거죠?

음..그런데 pointer의 pointer를 뭐라고 부르나요? 이중 포인터? 더블 포인터?

======================
BLOG : http://superkkt.com

violino의 이미지

우리말로 할땐 이중포인터, 영어로 할땐 더블포인터 ^^
이것도 job interview 할때 나왔던 문제네요 ;)

vio:

sunyzero의 이미지

double pointer 에 대해서 복잡하게 생각하는 경향이 짙은데, 이는 call by value 나 call by ref. 의 문제라고 할 수는 없습니다. 단지 함수 디자인에 따라서 결정됩니다. 다음과 같은 경우를 봅시다.

char *alloc_str(char * const str, const int len)
{
}

이 함수의 리턴값인 char * 형이 인수로 넘어가는 경우(reentrant 디자인, 바로 아래에 더 설명했음)에는 double pointer 가 되어야 합니다. 그 이유는 char * 를 리턴해야 하므로 이 주소값의 주소가 호출되어야 하기 때문입니다. 따라서 아래와 같은 형태로 바꾸어 쓸 수 있습니다. 이 경우에 ret_str은 앞의 리턴값이 인수로 들어온 형태입니다. 만일 NULL이 리턴되는 경우를 생각해보면 single pointer 가 지정되면 넘겨준 주소 자체를 잃어버리겠죠? 따라서 double pointer가 사용되는게 지극히 당연하다는 것을 알 수 있으실 겁니다.

/* ret_str 은 리턴값의 대치 */
int alloc_str(char **ret_str, char * const str, const int len)
{
}

이런 디자인의 함수는 REENTRANT 처리를 위한 함수에서 종종 등장합니다. reentrant 는 함수가 비동기적 호출로 인해서 재진입 될때 서로가 간섭되지 않아야 합니다. 그러기 위해서는 주소값을 리턴(대개 static 변수의 사용하는 리턴의 경우)가 제외되어야 하기 때문에 두번째 방식의 디자인을 선호합니다. 물론 위의 경우는 reentrant와 상관없이 별 문제가 없어보이지만, 일반적으로 쓰레드 함수의 경우를 생각해보면 리턴값의 형으로 포인터를 리턴하지는 않지요.

따라서 쓰레드 함수에서도 이와 비슷한 형태의 함수를 볼 수 있는데, 예로 pthread_join()함수가 있지요. 이 함수는 void * 형태의 리턴값의 주소를 가져와야 하기 때문에 void ** 형을 인수로 받습니다.

========================================
* The truth will set you free.

댓글 달기

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
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.