C 포인터에 관한 간단한 질문...

HiHi의 이미지

안녕하세요~
포인터 배열을 이용해서 프로그램을 짜고 있는데..
포인터 배열 대신에 더블포인터를 이용하려고 합니다.

그런데, 이상한점을 발견해 질문 드립니다.

int main()
{
    //char c[1024][12];
    char **a;

    *a = (char *)strdup( "hoho" );
    printf( "a[0] %s\n", a[0] );
}

위의 코드를 컴파일 해서 사용하면 이상없이 돌아가거든요.
그런데 char c[1024][12] 을 주석을 풀면, 세그멘테이션 폴트가 나는군요.
strdup() 에서 폴트가 발생합니다.

혹시 왜 이렇게 되는것인지, 조언 부탁드립니다.

bugiii의 이미지

char c[1024][12] 가 있든 없든간에

char **a;
*a = (char*)strdup( "hoho" );

이것이 정상적으로 보이지는 않습니다.

a 자체는 무엇을 가리키는 포인터가 맞습니다. 그리고 *a 도 무엇을 가리키는 포인터인 것도 맞습니다. 하지만 a 가 가리키는 주소를 저장할 공간은 변수 선언에서 마련되어 있는데, *a 가 가리키는 주소를 저장할 공간은 아직 없는 상태에서 그곳에 strdup 에서 리턴되는 주소값을 집어넣은 결과로 보입니다.

아마도 c 배열 때문에 우연으로 즉각적인 반응이 있었을 것으로 보이구요. 있든 없는 문제가 있는 코드입니다. c가 없었을 대 문제가 안생긴 건 우연일 가능성이 큽니다.

원하시는 것은 아마도 포인터의 배열이나 포인터의 리스트 같습니다. 하나의 더블 포인터를 가지고 여러개의 요소를 순회하는 것은 불가능하다고 생각합니다.

그럼, 이만...

p.s. 스택크기는 제가 숫자를 잘못 봤습니다. 죄송합니다. 역시 여기 bbs 의 알림기능은 대단합니다... 올리고 바로 수정했었는데... :oops:

HiHi의 이미지

음... 스택 사이즈를 보라는 말씀은
프로세스의 스택 사이즈를 넘어서 폴트가 발생한다는 말씀이신가요?

링커의 스택사이즈와 OS에서 제한하는 스택사이즈는 다른가 보죠?
죄송합니다. 제가 잘 몰라서요. 현재 OS에서 프로세스당
스택 사이즈는 8K 구요.

int main()
{
    //char c[1];        요기 주석을 풀면 실행은 되나, 실행 후 Aborted 라고 나옴
    //char c[2];        요기 주석을 풀어도 실행은 되나, 실행 후 Aborted 라고 나옴
    //char c[3];        요기 주석을 풀면 세그멘테이션 폴트가 발생합니다.
    char **a;

    *a = (char *)strdup( "hoho" );
    printf( "a[0] %s\n", a[0] );
}

왜 이런것인지... T-T
아, 플랫폼은 x86(펜3)에 gcc, Linux(Redhat 7.3) 입니다~

^^

케인의 이미지

char** a;

[포인터 변수 a] ---> [포인터 변수 a'] --> [char]

위와같이 2중 포인터 구조입니다.

a라는 변수는 a'를 가리키고 있게 됩니다. a'은 코드상에 없고,
실제로 할당되어있지도 않습니다.
할당되지 않은 주소에다가 strdup()의 결과로 리턴된 포인터값을
저장하려고 했기 때문에 에러가 발생하는 것입니다.
(에러가 발생 안하기도 하는 건 단순히 운이 좋았기 때문이죠)

다음과 같이 코딩하세요

a = malloc( sizeof(char*) * 10 ); 문자열 10개를 저장하는 포인터 배열

a[0] = strdup( "test1" )
a[1] = strdup( "test2" )
...
a[9] = strdup( "test9" )

10개만 malloc으로 할당했기 때문에 a[9]까지밖에 저장할 수 없습니다.
크기는 적절하게 정하시면 됩니다.

char a[10][1024] 를 의도하신 거라면

a[0] = malloc( 1024 ); //1024 바이트 길이의 버퍼를 10개 생성
a[1] = malloc( 1024 );
...
a[9] = malloc( 1024 );

이렇게 하시면 됩니다.

char a[1024][10]; 은 10바이트짜리 버퍼 1024개를 의미합니다.

lunarainbow의 이미지

메모리 할당 문제이네요..

char *strdup(const char *s);

RETURN VALUE
       The  strdup()  function  returns a pointer to the duplicated string, or
       NULL if insufficient memory was available.

이렇게 있는데, strdup 함수는 새로 malloc을 해서 문장을 카피한 후, 그 주소를 반환하게 되잖아요.

그런데..

char **a;

*a = xxxxxxx;

이렇게 쓰시면, a라는 포인터가 가르키는 어떤 주소의 값에 넣으라. 라는 의미가 되는 것인데,

현재 a는 아무 주소도 가르키고 있지 않았잖아요. 만약 NULL(또는 기타 이상한 값)이라 가정하였을 경우,

*a = xxxxx;

의 의미는,

주소번지 NULL(또는 이상한 주소의 메모리)을 찾아가서, 그것의 값을 이 주소값(리턴받은 주소값)으로 셋팅해라.

라는 의미가 되겠죠?

한번 이렇게 사용해 보세요.


int main() 
{ 
    //char c[1024][12]; 
    char **a; 

    a = (char**)malloc(sizeof(char*) * 1);
    *a = (char *)strdup( "hoho" ); 
    printf( "a[0] %s\n", a[0] ); 
} 

이렇게 하시면 될듯 싶네요. ^^

HiHi의 이미지

답변 감사드립니다~ ^^
큰 도움이 되었습니다. 역시, 고수님들이 많으시군요~

제가 질문을 드린 이유가 포인터 배열을 더블포인터로 대신할려는 거였거든요.
이렇게 하려는 이유가, 포인터 배열로 하려면, 미리 포인터 배열의
배열값( 예를들면, char *a[8192] )을 명시해야 하는데, 아무래도
메모리 낭비같아서요.

답변 해주신 코드도, 더블포인터 사용전에
메모리 할당으로 크기를 명시해줘야 하더군요.

int main() 
{ 
    char **a; 

    a = (char**)malloc(sizeof(char*) * 1); 
    *a = (char *)strdup( "hoho" ); 
} 

위와 같은 코드는 포인터배열로 치면 1개만 사용하는거구..

int main() 
{ 
    char **a; 

    a = (char**)malloc(sizeof(char*) * 1000); 
    *a = (char *)strdup( "hoho" ); 
} 

위와 같은 코드는 포인터배열로 치면 1000개를 사용하는거잖아요..

근데 문제가, 현재 몇개를 할당해야 할지 알수가 없습니다.
다음과 같은 작업을 하려고 하거든요.

int parseString( char *msg )
{
    int aCnt, iCnt;
    char **a;

    aCnt = iCnt = 0;
    a = (char**)malloc(sizeof(char*) * 1000);   /* 크기를 1000 로 명시 함 */
    while( msg[iCnt] != '\0' ) {
        if( msg[iCnt] == '^' ) {
            *(a+aCnt) = (char *)strdup( "hoho" );
            aCnt++;
        }
        iCnt++;
    }
}

위와 같이, msg를 파싱을 하는데, msg의 '^'를 딜리미터로 사용하여,
그 값마다, 포인터배열(or 더블포인터)에 넣으려고 합니다.
위와 같은 코드라면, msg에 '^' 갯수가 1000 개를 넘어가면, 에러가 나겠죠.
'^' 딜리미터가 나올때마다 메모리 할당하면서, 하는 방법이 없을런지요?

그냥 편하게, msg에서 ^ 갯수를 조사한 담에, 그 갯수만큼 메모리 할당하고,
파싱을 시작하면 쉽겠지만, 그럴수 있는 상황이 아니어서요.

파싱을 하면서 동시에, 메모리 할당(배열 수)을 하려는게 제 의도입니다.
조언 부탁드립니다.

^^

dondek의 이미지

realloc() 을 써보세여. 아니면 흔한 linked list를 써보셔도 될듯하네여.

진리를 나의 수준으로 끌어내리지 마라.
나를 진리의 수준으로 끌어올려라. - 배꼽 중에서

moonzoo의 이미지

malloc은 동적 메모리 할당..즉 가변적인 상황에 대처하기 위함입니다.

그런데 주어진 parseString(...) 을 보니

a = (char**)malloc(sizeof(char*) * 1000); /* 크기를 1000....*/

이렇게 1000을 미리 잡아 놓으셨는데..

이것보다는 strdup를 통해 할당하기 직전에서

*(a+aCnt) = (char *)malloc(sizeof(char *)); 하시면 될듯 합니다..

int parseString( char *msg ) 
{ 
    int aCnt, iCnt; 
    char **a; 

    aCnt = iCnt = 0; 
    
    while( msg[iCnt] != '\0' ) { 
        if( msg[iCnt] == '^' ) { 
            *(a+aCnt) = (char *)malloc(sizeof(char *));
            *(a+aCnt) = (char *)strdup( "hoho" ); 
            aCnt++; 
        } 
        iCnt++; 
    } 
} 
lunarainbow의 이미지

미리 필요한 스트링의 갯수를 모른다면, 어찌 할 수 없네요.

지금 생각나는 적당한 방법은 2중 포인터의 링크드 리스트 정도겠네요.

typedef struct link
{
    char **a;
    struct link *next;
} link;


main()
{
    link *start=NULL, *temp=NULL;

    
  
    while( msg[iCnt] != '\0' ) { 
        if( msg[iCnt] == '^' ) { 
            if ( start == NULL ) {
                 temp = start = (link*)malloc(sizeof(link));
                 if ( start -> a == NULL ) {
                      start -> a = (char**)malloc(sizeof(char*) * BUFCNT);
                 }

                 aCnt++; 
            }
            if ( aCnt == BUFCNT )
            {
                  temp -> next = (link*)malloc(sizeof(link));
                  aCnt=0;
                  temp = temp -> next;
                  temp -> a = (char**)malloc(sizeof(char*) * BUFCNT);
            }
            temp -> a + aCnt = (char*)malloc(BUF_LEN);
            *(temp -> a+aCnt) = (char *)strdup( "hoho" );              
        }
        iCnt++; 
    } 
} 

지금 수업을 들어가야 해서.. 자세한 설명은 드리지 못하겠고, (프로그램이 맞았는지도 확신 못하겠네요..;; 시간상. ㅡㅜ )

아무튼 이런 식으로 하는것이 좋지 않을까 싶네요.

그냥 완전히 링크드 리스트로 작성하면 좀...;;

이왕이면 저기 스트링의 malloc도 하나로 해버리고 싶은데, 있다 수업 끝나고 와도 해결 안되어 있다면, 그때 다시 올리겠습니다. ^^

bugiii의 이미지

list<string> 사용하시면 안될까요?

HiHi의 이미지

답변 해주신분들 정말 감사드립니다. ^^
그런데, 제가 원하는 답을 아직 못찾았습니다. T-T

먼저, realloc()는 정상적으로 작동을 하지 않습니다.
제가 예를 들어서 코드를 만들어 봤습니다.

int parseString( char *msg )
{
    int aCnt, iCnt;
    char **a;

    aCnt = iCnt = 0;
    a = (char**)malloc(sizeof(char*) * 5);
    //realloc( a, sizeof(char *) * 1024 );         /* 요기서 realloc() 하면 잘됨. 그러나 realloc의 의미가 없음. */
    while( msg[iCnt] != '\0' ) {
        if( msg[iCnt] == '^' ) {
            *(a+aCnt) = (char *)strdup( "hoho" );
            aCnt++;
            if( aCnt == 1 ) {
                realloc( a, sizeof(char *) * 1024 );       /* 요기서 realloc() 하면 parseString END를 찍고 폴트가 납니다. */
            }
        }
        iCnt++;
    }
    printf( "parseString END\n" );
}

int main()
{
    char tmpBuf[1024];

    memset( tmpBuf, '^', sizeof(tmpBuf)-1 );
    tmpBuf[sizeof(tmpBuf)-1] = '\0';

    parseString( tmpBuf );
    printf( "Main END\n" );
}


먼저, realloc()은, strdup() 다음에 쓰면
parseString()함수가 끝나고 "parseString END\n"를 찍은 후에,
폴트가 납니다, 아무래도 제 생각엔,
a 배열의 스택 버퍼 오버 플로우가 발생해서, 함수 리턴주소를 못찾는듯..
얼래? heap메모리에 할당 했는데, 스택 오버 플로우가 날수도 있나?
잘 모르겠슴다.. T-T 아무튼 realloc() 는 안되요~

그리고, linked list는 포인터 배열의 의미가 없어져버립니다.
제가 굳이 포인터 배열을 쓸려는 이유가, 특정 위치의 값을
바로 찾으려고 하거든요. 예를 들어,
a의 100번째 값을 찾는다고 하면, 포인터 배열(더블포인터)은 a[99] 하면 되지만,
linked list는 루트를 돌면서 찾아야 하는 오버헤드가 있네요.
솔직히 100 이라면, 별 오버헤드가 없겠지만, 몇개가 될지 몰라서요.

그리고, 답변해 주신

while( msg[iCnt] != '\0' ) { 
        if( msg[iCnt] == '^' ) { 
            *(a+aCnt) = (char *)malloc(sizeof(char *)); 
            *(a+aCnt) = (char *)strdup( "hoho" ); 
            aCnt++; 
        } 
        iCnt++; 
}

이 코드는, 좀 이상하네요...
돌다 뻑나요. 폴트로..

list<string> 는 c++인가요?
c++은 못쓰거든요..으아~

Linked list 말고는 답이 없는걸까요?

^^

mrchu의 이미지

이렇게 해보시면 어떨지..

#include <stdio.h>

int parseString( char *msg )
{
    int aCnt, iCnt, i;
    int default_size = 5;
    char **a;

    aCnt = iCnt = 0;
    a = (char**)malloc(sizeof(char*) * default_size);
    while( msg[iCnt] != '\0' )
    {
        if( msg[iCnt] == '^' )
        {
            *(a+aCnt) = (char *)strdup( "hoho" );
              aCnt++;

            if( aCnt >= default_size )
            {
                ++default_size;
                a = (char**)realloc( a, sizeof(char *) * default_size );
            }
        }
        iCnt++;
    }
    printf( "parseString END\n" );
    /* testing */
    for(i = 0;i < aCnt;++i)
    {
            printf("\n %s", *(a + i));
    }
}

int main()
{
    char tmpBuf[1024];

    memset( tmpBuf, '^', sizeof(tmpBuf)-1 );
    tmpBuf[sizeof(tmpBuf)-1] = '\0';

    parseString( tmpBuf );
    printf( "Main END\n" );
}
moonzoo의 이미지

Quote:
이 코드는, 좀 이상하네요...
돌다 뻑나요. 폴트로..

아..제가 착각햇네요..잘못된 코드였음..

mrchu의 이미지

realloc은 문제를 많이 일으키기로 소문난 함수고요,
실제로
새 크기로 malloc
기존의 버퍼 새 버퍼로 copy
기존의 버퍼free
를 자례대로 하는것과 마찬가지거든요.
한번 호출 할때마다, 복사를 수행하게 되므로, 퍼포먼스가 크게 떨어집니다.
링크드 리스트 계열을 사용하시는 것이 좋은 선택 같습니다.

아니면 C++을 사용하셔서 STL을 사용하시면 아주 편합니다.

추신 생각해 보니 realloc하는게 포인터에대한 포인터이므로, 복사에 대한 부담이 크지는 않겠군요. 물론 포인터라해도 개수가 아주 많다면 부담이 되겠지요.

HiHi의 이미지

답변 감사드립니다. ^^
realloc()로 되는군요.

제가쓴 realloc은 문제가 있었네요. 흐~

근데, realloc은 퍼포먼스에 문제가 있군요.
realloc이 내부적으로, free AND copy라면,
포인터라고 해도, 나중에 문제가 있을듯 합니다.
free AND copy가 자주 일어나면, 메모리 fragmentation 때문에,
후에 OS 상에서도 CPU를 많이 먹을듯.. 그렇지 않은가요?
아니면, 특정 크기( 128,256,512,... )로 바운더리를 둬서, 그때 만 realloc을 해주면 괜찮을것 같기도 하네요.
리눅스가 메모리 관리를 얼마나 잘 해줄런지..

음.. Linked list와 얼마만큼의 차이가 있을지는,
음.. 실험을 해봐야 겠습니다.

답변 주신분들께 정말 감사드립니다~

그럼~ 수고하세요~

^^

mrchu의 이미지

해결 되셨다니 다행입니다.
그런데 쓰신 글중에, 이중 포인터를 사용하신 이유가, 각 항목에 대한 빠른 액세스가 필요하셔서 그렇다고 하신 점을 제가 깜박 했군요.
만일 상수시간의 빠른 액세스가 필요하시다면, 배열밖에 방법이 없겠네요.
링크드 리스트는 삽입/삭제가 상수 시간에 이루어 지는 대신에, 액세스 시에 앞에서부터 차례대로 찾아 나가야 하니까요.

일정 바운더리를 두어서 그것이 넘을때만 realloc을 수행하는것도 좋은 생각이신것 같습니다. 실제로 많이 쓰이는 방법이기도 하고요. STL의 vector같은것도 같은 방식으로 구현되어 있는 것으로 알고 있습니다.

삽입/삭제 시간이 중요한지, 아니면 접근 시간이 중요한지 따지셔서 선택하시는게 좋을것 같습니다.

HiHi의 이미지

조언 감사드립니다~
참고 하도록 하겠습니다~ ^^

^^

댓글 달기

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