pointer에 대한 난해한 질문입니다.

김경태의 이미지

1. 프로그램

main()
{
    int i, j;
    int *a[3];
    int (*b)[3];

    b = ( int (*) [3] ) malloc(sizeof(int (*) [3] ));
    printf("after malloc:(b) is %d, (b+1) is %d\n", b, b+1);
    
   /* 0부터 2까지 b에 할당된 메모리에 i의 증가값을 대입후 print */
    for(i = 0; i < 3; i++ )
    {
        *((char *)b + i*sizeof(int)) = i;
        printf("b[%d]=%d\n", i, *((char *)b + i*sizeof(int)));
        printf("b[%d] addr is %d\n", i, ((char *)b + i*sizeof(int)));
    }

    /* 0부터 2까지 b에 할당된 메모리의 주소를 a에 할당한 후
        *연산자를 이용하여 그 내용을 가져오려 시도함 */
    for(i=0; i < 3; i++)
    {
        a[i] = (int *) ((char *)b + i*sizeof(int));
        printf("a[%d] is %d\n",i, a[i]);
        /* 주목!!!!! */
        printf("*a[%d]=%d\n", i, (int)*a[i]);
    }

puts("-------------------------------------------\n");

    /* b를 array형태로 접근 */
    for(i = 0; i < 3; i++ )
    {
        b[0][i] = i;
        printf("b[%d]=%d\n", i, b[0][i]);
        printf("b[%d] addr is %d\n", i, &b[0][i]);
    }

    /* array형태로 접근한 b의 내용을 a를 이용하여 print */
    for(i=0; i < 3; i++)
    {
        a[i] = (int *) ((char *)b + i*sizeof(int));
        printf("a[%d] is %d\n",i, a[i]);
        printf("*a[%d]=%d\n", i, (int)*a[i]);
    }

    free(b);
}

2. 결과

after malloc:(b) is 133656, (b+1) is 133668
b[0]=0
b[0] addr is 134104
b[1]=1
b[1] addr is 134108
b[2]=2
b[2] addr is 134112
a[0] is 134104 <== adress 이상무!
*a[0]=133664 <== 원하는 결과는 0
a[1] is 134108 <== adress 이상무!
*a[1]=16777216 <== 원하는 결과는 1
a[2] is 134112 <== adress 이상무!
a[2]=33554440 <== 원하는 결과는 2

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

b[0]=0
b[0] addr is 134104
b[1]=1
b[1] addr is 134108
b[2]=2
b[2] addr is 134112
a[0] is 134104
*a[0]=0 <== 어라! 원하는 결과 !
a[1] is 134108
*a[1]=1 <== 어라! 원하는 결과!
a[2] is 134112
*a[2]=2 <== 어라! 원하는 결과!

3. 질문

위에서 분명히 연속된 b의 메모리 구조에 적절한 값을 assign하고 이것을 다
시 print하는 것은 성공했습니다.

b의 address를 a에 할당하는 것도 성공했습니다.

그런데....

b의 address가 할당된 a를 *연산자를 사용하여 그 내용을 보려하면 왜 엉뚱
한 결과가 나올까요?

분명히 a[i]에는 b의 address가 제대로 할당된 것이 맞는 것 같은데요.

그리고....

b의 내용을 할당할때 b[0][i]로 할당하면 *a[i]의 접근이 가능한데, 왜
*((char *)b + i*sizeof(int)) = i; 와 같이 값을 할당하면 *a[i]의 접
근이 불가능할까요?

어차피 b의 내용을 할당할때 어떻게 할당하던지 주소는 같은데 말입니
다....

이에 관하여 현명한 고수님의 훌륭한 가르침을 기대합니다. ^^;

fox9의 이미지

맨 아래에서 4번째 줄을 아래와 같이 고치면 어떻게 되는지요?
printf("*a[%d]=%d\n", i, (int)(*a[i]));

위의 괄호가 없다면 우선순위에 의해서 a[i] 포인터의 int * 값이 되는건 아닌가요?
제가 잘못알고 있는 것인지...
하여튼 ㅡ.ㅡ

arimae의 이미지

우선 선언을 보면.

int *a[3]; 
int (*b)[3]; 

이미 아시겠지만, 위의 두개의 선언은 성격이 다릅니다. 포인터 공부하시는 분이
초기에 공부하다가 잘 헷갈리시는 포인터의 배열과 배열의 포인터입니다.
처음것이 포인터의 배열입니다. 두번째 것이 배열의 포인터구요...
차이점은 첫번째 것은 int에 대한 포인터가 세개 있다는 것입니다. 하지만 두번째 것은
3의 크기를 가진 배열에 대한 포인터이구요.
그림을 그려 나타내면

첫번째 것... *는 int형의 byte 크기만큼의 메모리 공간입니다.

************
***
*****

각 행의 길이는 할당한 메모리 공간만큼 이며 각 행의 길이는 같지 않을 수도 있습니다.

두번째 것..

***
***
***
***
.
.

이렇게 각 행의 길이는 3이며 행의 개수는 할당한 메모리 공간만큼 입니다.
둘다 2차원 배열의 형태로 접근할 수 있습니다.
따라서 b에다가 메모리 할당을 할때 할당 크기부터 잘못된것 같습니다.

b = ( int (*) [3] ) malloc(sizeof(int (*) [3] ));

sizeof(int(*)[3])의 크기는 4 byte입니다. (포인터의 크기) 하지만 b는 각 행마다
최소한 3개의 int 를 저장할 공간을 가지고 있어야 하므로.

b = ( int (*) [3] ) malloc(sizeof(int (*) [3] ) * 3); 위의 처럼 12 byte 의 공간을 최소한 할당해 줘야 합니다.

그리고 a에다가 b를 assign 할 목적이므로 3행이 필요합니다.
따라서 역시 위의 공간 * 3 만큼 더 할당해 줘야 합니다.

b = ( int (*) [3] ) malloc(sizeof(int (*) [3] ) * 3 * 3);

이렇게 하면 총 36 byte가 할당이 됩니다.

이제 대입하는 부분을 살펴보면,

*((char *)b + i*sizeof(int)) = i;

위의 코드를 보면 상당히 위험한 것 같습니다. b는 int의 포인터입니다.
이것을 강제로 char 포인터로 변환을 시킵니다. 이러다가 잘못해서 (char *) b + 1
과 같은 코드를 쓴다면 illegal bus error 가 날지도 모릅니다. 지금은 다행히
4byte를 곱해주므로 그런 에러는 나지 않습니다.
그리고 어차피 포인터는 배열과 같은 방법으로 사용할 수 있습니다.
지금 b는 이차원 포인터지만 3 X 3 크기의 이차원 배열로 접근할 수 있습니다.
따라서 위의 코드와 같이 포인터 연산으로 하느니 차라리 b[0] 이런식으로
접근하는 것이 훨씬 더 직관 적입니다.
위의 코드를 casting 하지 않고 원래 대로 쓰면

*(b + i) = i;

이렇게 될것입니다. 하지만 이것은 올바른 포인터 연산이 아닙니다.
왼쪽을 배열형식으로 쓰면 b[0] 가 됩니다. 이 것은 첫번째 행의 주소를 의미합니다.
이렇게 왼쪽에 대입할 경우 첫번째 행의 주소를 변환시키겠죠.

int *a;

// 올바른 코드
*a = 2;

// 틀린 코드. 위의 *(b + i) = i; 라면 아래와 같은 의미가 됩니다.
a = 2;

따라서 정확히 값을 대입하려면

*(*(b + i)) = i;

가 되야 합니다. 이렇게 하면 첫번째 행 첫번째 열의 장소에 값을 저장하라는 의미가 되겠죠.

*b[0] 나 b[0][0] 도 같은 의미가 됩니다.

따라서 이런식으로 고쳐 나간 코드는 아래와 같습니다.
대입한 값은 결국 전체 3 X 3 배열에서 첫번째 열에만 0, 1, 2를 대입한 것과 같은 효과가 되는군요...
그리고 b 자체가 3개의 일차원 배열에 대한 배열이므로 a에서 a+1로 갈때 크기는 12가 증가해야 맞습니다.

#include <stdio.h>
#include <stdlib.h>

main() 
{ 
    int i; 
    int *a[3]; 
    int (*b)[3]; 

    b = ( int (*) [3] ) malloc(sizeof(int (*) [3] ) * 3 * 3); 
    printf("after malloc:(b) is %d, (b+1) is %d\n", b, b+1); 
    
   /* 0부터 2까지 b에 할당된 메모리에 i의 증가값을 대입후 print */ 
    for(i = 0; i < 3; i++ ) 
    { 
        *(*(b + i)) = i; 
        printf("b[%d]=%d\n", i, *(*(b + i))); 
        printf("b[%d] addr is %d\n", i, *(b + i)); 
    } 

    /* 0부터 2까지 b에 할당된 메모리의 주소를 a에 할당한 후 
        *연산자를 이용하여 그 내용을 가져오려 시도함 */ 
    for(i=0; i < 3; i++) 
    { 
        a[i] = *(b + i); 
        printf("a[%d] is %d\n",i, a[i]); 
        /* 주목!!!!! */ 
        printf("*a[%d]=%d\n", i, (int)*a[i]); 
    } 
    free(b); 
}

지금보니 할당할때 사이즈가 잘못되었군요. 실수했습니다.
지금 고쳐놨습니다.

Dream, Passion and Challenge..

운형의 이미지

위에 답글 다신분이 설명을 잘해노셔서 내용은 없음당.

Do you think that's the air you are breathing now?

댓글 달기

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