IT 과학/C언어

C언어 | 포인터

곰뚱 2019. 12. 25.

 

 

 

포인터라는 개념은 C의 대표적인 특징 중의 하나로, 포인터를 쓰면 여러 가지 형의 데이터를 마음대로 참조할 수 있다. 그러나 사용 방법이 틀리면 프로그램이 폭주하거나 이해하기 어려운 프로그램이 만들어질 위험성이 있다. C를 처음 공부하는 대부분이 최초로 만나는 벽이 포인터라고도 한다. 이번 주 강의에서는 포인터의 개념과 사용법에 관해서 설명하고, 포인터와 배열, 포인터와 문자열의 관계에 대해서도 설명한다.

 

 

1. 포인터란?

포인터(pointer)는 C의 대표적인 특징이다. 포인터를 사용함에 따라서 메모리상의 데이터를 쉽게 사용할 수 있다. 잘못 이해하면 수정이 곤란한 프로그램을 작성할 가능성이 있지만, 포인터 사용법에 익숙해지면 명쾌하고 훌륭한 프로그램을 작성할 수 있다.

 

(1) 포인터의 선언

포인터 변수는 포인터(주소값)를 저장하는 변수로 다음과 같이 *을 붙여 선언한다.

int *pa;

이것은 변수 pa가 int형 데이터를 참조하기 위한 포인터 변수인 것을 의미한다. 즉 pa는 (int *)형의 변수를 선언하고 있는 것이다. 여기서 주의할 사항은 포인터 변수명은  pa이며 *pa는 아니라는 것과 포인터 변수 pa의 형이 int형이 아니고 pa에서 참조할 수 있는 데이터형이 int형이라는 것이다. 따라서 char형 데이터를 지정하는 포인터 pb는 다음과 같이 선언된다.

 

char *pb;

 

그러면 (int *)형의 포인터 변수 pa와 (char *)형의 포인터 변수 pb가 참조할 수 있는 데이터가 각각 int형과 char형이라는 것이며 포인터 변수의 크기는 모두 주소를 넣어두는데 필요한 크기로 대개 int형과 같은 크기이다.

 

(2) 포인터 연산자

포인터 연산을 위해 다음 두 가지 연산자가 필요하다. 첫 번째로 포인터가 지정하는 내용을 갖는 내용(content) 연산자 *와 두 번째로 변수가 할당되어 있는 메모리상의 주소값을 갖는 주소(address) 연산자 &이다. 예를 들어, (int *)형의 포인터 변수 pa에 대하여 (위에서 선언된 포인터 변수를 가정하자)

 

pa = &a;

 

라고 하면, 변수 a의 메모리상의 주소가 pa에 대입된다. 이렇게 처리한 후, *pa라 하면 pa가 지정하는 메모리의 데이터를 얻을 수 있다. 따라서

b = *pa;

은 변수 a 의 내용이 변수 b에 대입된다.

 

 

2. 포인터와 문자열

C로 문자열을 취급할 경우에는 포인터를 이용하면 편리하다. 문자열을 Hyolee로 정의하고자 하면,

 

char *str;
str = "Hyolee";

 

라고 표시한다. C 컴파일러는 Hyolee라는 문자열을 메모리에 저장하고 마지막에 '\0'을 첨가시킨다. 그리고 str="Hyolee"에 의하여 "Hyolee"를 지정할 문자열이 저장된 메모리의 시작 주소를 포인터 str에 전달한다. 즉, "Hyolee"라는 문자열 상수는 그 문자열이 저장되어 있는 시작 주소를 식의 값으로서 보유하고 있는 것이지 문자열의 실체가 아니라는 것에 주의해야 한다.

그러면 포인터 str은 문자열 "Hyolee"의 처음 주소(시작 주소, 문자열의 첫 번째 문자의 주소)를 지정하고 있으므로 *str이라고 하면 문자 'H'를 취하게 된다. 여기서 str++ 로 하면 포인터 변수 str의 값이 +1씩 증가되므로 이 때의 *str은 다음 문자(y)를 가리키게 된다. 따라서 str로 지정하는 문자열을 차례대로 취해 가려면 다음과 같이 된다. (문자열을 메모리에 저장하고 마지막에 '\0'을 첨가시킨다는 것을 상기하자)

         while(*str != ' \0'){
                  
              str++;
         }

 

예제 1. 문자열의 문자를 1문자씩 취해서 표시하여라.

● 프로그램

#include <stdio.h>
#include <string.h>
main()
{
   char *str;
   str = "Hyolee";      /* ① */
   while (*str != '\0') {   /* ② */
       printf("%c\n", *str);  
       str++;  /* ③ */
  }
}

 

● 해설

  ①  포인터 str 에는 문자열 "Hyolee"의 처음 주소(첫번째 문자의 주소)가 대입된다.

  ②  *str 에 의하여 str로 표시되는 주소의 문자를 취한다.

  ③  포인터를 다음 문자로 진행시킨다. 즉 str에 저장되어 있는 주소값이 +1씩 증가된다.

 

● 결과
H
y
o
l
l
e

 

<참고> 문자열이란?

문자열(character string)이란 하나 이상의 문자들의 연속이다.

"I love you."
                

I

 

l

o

v

e

 

y

o

u

.

\0

 

1. 문자열은 문자들의 배열로 저장된다.

예)  char name[40];

name[0] = 'I';

name[1] = ' ';

name[2] = 'l';

name[3] = 'o';

....

name[10] = '.';

name[11] = '\0';

 

2. 문자열의 끝은 null 문자('\0')로 표시한다.

 

3. 문자 'X'와 문자열 "X"는 다르다.

 

문자 'X'의 메모리 상태

 

X

 

문자열 "X"의 메모리 상태

 

X

\0

 

예제 2: 문자열을 사용한 프로그램 예

#include <stdio.h>

#define MESSAGE "Welcome to the C programming world !!!"

main()  /* Example of a character string */
{
        char name[30];

        printf("Input your name : ");
        scanf("%s", name);
        printf("Hello %s, %s\n", name, MESSAGE);
}

 

● 결과

Input your name: Hyolee (사용자가 입력한 자신의 이름)

Hello Hyolee, Welcome to the C programming world!!!

728x90

 

 

3. 포인터와 배열

(1) 포인터와 1차원 배열

C에서는 포인터를 사용해서 배열의 요소를 참조할 수 있다. 따라서 포인터와 배열은 밀접한 관계가 있다. 예를 들어

 

int a[10];

 

라는 배열이 있을 때

 

int *pa;

 

라는 포인터 변수를 선언하고   

  

pa = a;

 

라고 하면 포인터 pa를 사용해서 배열 a[0]∼a[9]를 참조할 수 있다. 배열명 a는 배열의 시작 주소(첫번째 요소의 주소)를 지정하는 포인터 상수라고 생각할 수 있다. 따라서 pa=a;에 의하여 배열의 시작 주소가 pa에 전달되기 때문에 *pa라고 하는 것에 따라 a[0]의 내용을 참조할 수 있게 된다. *(pa+i)라고 하면 i+1번째의 요소 a[i]가 참조된다. 다시 포인터 변수 pa를 사용해서 pa[0], pa[1]… pa[i]라는 표시가 가능하다. [ ]는 주소 계산을 하는 연산자로 생각할 수 있다.

 

여기서 주의할 사항은 pa[0]∼pa[9]라는 영역이 새롭게 확보되는 것이 아니라 기존에 존재하는 배열 a를 포인터 변수 pa를 사용해서 a[0] ∼a[9]를 참조한다는 것이다.

 

(2) 포인터와 2차원 배열

 2차원 배열과 포인터의 관계를 살펴보자. 가령

 

int a[4][3];

 

이라고 2차원 배열을 선언했을 때 메모리상에는 a[0][0]∼a[3][2]까지의 각 요소들이 일렬로 확보된다.

 

a[0][0]

a[0][1]

a[0][2]

a[1][0]

a[1][1]

a[1][2]

a[2][0]

a[2][1]

a[2][2]

a[3][0]

a[3][1]

a[3][2]

 

지금

 

int *pa;

 

라는 포인터를 선언하고,

 

pa = a;

 

라 하면, 포인터 변수 pa에 2차원 배열의 시작 주소를 전달할 수 있다. 이렇게 한 후 *pa라고 하면, a[0][0]의 내용을 취하고,*(pa+3)라고 하면 a[1][0] 의 내용을 얻을 수 있다. 이와 같이 포인터를 사용해서 2차원 배열을 직접 참조할 수 있다. C에 있어서 2차원 배열 a[i][j]의 a[i],a[i][j]는 각각 서로 다른 의미를 갖고 있다. 배열명 a는 2차원 배열의 선두 번지를 표시하는 상수이고 a[i]는 i+1번째 행(a[i][0], a[i][1])의 선두 번지를 표시하는 상수이며, a[i][j]는 i+1번째 행 j+1번째 열의 요소를 가리키는 변수이다. 따라서


   
pa = a[1];

 

라 하고, *pa 로 하면 이것은 a[1][0]을 참조하는 것이 된다. 이와 같이 C에서는 배열의 참조를 융통성 있게 실행하도록 a(i,j)라는 표현은 사용하지 않고 a[i][j]라는 분리 가능한 표현을 한다. 더욱이 a나 a[i]는 상수이므로 이것들에 대한 대입은 실행이 안 되기 때문에 주의해야 한다.

 

확인문제 1

/* 배열의 각 원소의 주소를 출력하는 프로그램 */

#include <stdio.h>

#define SIZE 4

void main(void)

{

      int    array[SIZE] = {5, 10, 15, 20}, index;

      double real[SIZE]  = {5.5, 10.10, 20.20, 30.30};

 

      for(index = 0; index < SIZE; index++)

          printf("Address of array[%d] is %u === Adress of real[%d] is %u\n",

                index, array + index, index, real + index);

}

 

이 프로그램의 의도는 comment에 있는 것처럼 배열의 각 원소의 주소를 출력하는 프로그램입니다. 즉, 실행 결과에서 보듯이 배열의 시작 주소인 배열명(포인터 상수)에 1을 더하면, 각 원소의 자료형의 크기인 바이트 수만큼 주소가 증가한다는 것을 알 수 있지요. int 형은 (터보 씨의 경우) 기억 장소를 2바이트 사용하여 표현되고, double 형은 8바이트를 사용하여 표현되므로 포인터에 1을 더한다고 할 때 int 형의 바이트 크기인 2만큼 증가되고 double 형의 경우에는 8만큼 증가됩니다. 다시 말하면 포인터에 1을 더한다는 말은 배열의 한 원소의 크기만큼 증가한다는 것입니다. 배열의 시작 주소를 나타내는 배열명(이 프로그램에서는 array)에 index만큼을 증가시킨다는 것은 다음과 같은 의미를 가집니다.

 

array + index <=> &array[0] + index *sizeof(배열의 자료형)

 

따라서 이 프로그램의 실행 결과에서 보듯이 int형 포인터가 1만큼 증가되면 int 형의 바이트 크기인 2 바이트만큼 주소가 증가되고, double 형 포인터가 1만큼 증가되면 double 형의 바이트 크기인 8만큼 증가됩니다.(기억 장소의 주소는 바이트 단위로 할당되는 것 아시죠?)



기억 장소에 할당된 배열의 각 원소에 저장된 데이터를 참조할 수 있는 방법은 3가지 방법이 있습니다.



(1) 참조하고자 하는 배열 원소의 첨자를 사용한다. --- array[i]

(2) 배열명과 간접 연산자를 사용한다. --- *(array + i)

(3) 포인터 변수에 배열을 할당하고 간접 연산자를 사용한다. --- *(pt + i)

즉, 위의 3가지 참조 방법으로부터, 배열의 i번째 원소에 저장된 값을 얻고자 한다면 다음과 같이 할 수 있습니다.

 

array[i] <=> *(array + i) <=> *(pt + i)

 

유사하게 컴퓨터 내부의 기억 장소에 할당된 배열의 각 원소에 대한 주소는 다음과 같이 3가지 방법에 의해서 얻을 수 있습니다.

 

(1) 배열 원소에 주소 연산자('&')를 사용한다. --- &array[i]

(2) 배열명을 사용하여 특정 원소(i)의 주소를 구한다. --- array + i

(3) 포인터 변수에 배열을 대입한 다음 배열의 특정 원소의 주소를 포인터 변수를 사용하여 계산할 수 있다. --- pt + i

 

이상의 결과로 부터 배열의 i번째 주소를 얻고자 한다면 다음과 같이 할 수 있습니다.

 

&array[i] <=> array + i <=> pt + i

 

확인문제 2

/* 1차원 배열과 포인터의 관계 예 */

#include <stdio.h>

#define SIZE 4

void main(void)

{

      int    array[SIZE] = {5, 10, 15, 20}, i, *pt;

      /* Data print */

      pt = array;

      for(i = 0; i < SIZE; i++)

          printf("array[%d] = %2d, *(array + %d) = %2d, *(pt + %d) = %2d\n",

                  i, array[i], i, *(array + i), i, *(pt + i));

      putchar('\n');

      /* Address print */

      for(i = 0; i < SIZE; i++)

          printf("&array[%d] = %u, array + %d = %u, pt + %d = %u\n",

                  i, &array[i], i, array + i, i, pt + i);

}

 

(3) 문자열 복사

문자열을 복사하는 경우를 생각해 보자. char *str; 라고 선언된 포인터에 str="Hyolee"; 라고 하는 경우 문자열의 실체를 복사하는 것이 아니고 단순히 문자열의 처음 주소(첫번째 문자의 주소)를 대입하고 있다는 것이다. 문자열의 실체를 복사 이동하는 곳은 포인터가 아닌 배열이어야 한다. 여기서 간단히

 

char s[80];

 

로 선언하고

 

s = "Lee Hyolee";

 

라고 지정하는 것은 잘못된 것이다.  배열명 s는 상수이므로 이러한 대입은 안된다. 따라서 문자열을 배열 s[ ]에 복사하는데는 다음과 같이 1문자 단위로 복사해야만 한다.

 

      i = 0;
      while(*str != '\0') {
          s[i] = *str;    
          i++;   
          str++;
      }  
      s[i] = '\0';

 

문자가 '\0'일 때는 s[i]에 대입하지 않고 루프를 빠져 나오므로 루프 밖에서 '\0'을 첨가시켜야 한다. 위의 프로그램은 다시 다음과 같이 쓸 수 있다.

 

       i = 0;
       while(*str != '\0'){
           s[i++] = *str++
       }
       s[i] = '\0';

 

또 다시 더욱 간단하게


   
        i = 0;  
        while((s[i++] = *str++) != '\0')
          ;

 

라고 쓸 수 있다.

 

예제 3. 포인터를 이용해서 문자열을 배열로 복사하여라.
  
● 프로그램
#include <stdio.h>
main()
{
         char *str, *ds, s[80];
         str = "Hyolee zzang!";
         ds = s;
         while ((*ds = *str) != '\0') {
             ds++;
             str++;  
         }  
         printf("%s\n",s);
 }

 

● 결과

 Hyolee zzang!

 

 

4. 포인터의 배열

C에서는 포인터 변수의 배열도 가능하고 이것은 여러 개의 문자열 데이터를 참조하는 경우에 많이 사용된다. 포인터 배열은

 

char *name[3];

 

으로 선언한다. 이것에 의하여 name[0], name[1], name[2]라는 3개의 char형 데이터를 참조하는 포인터 배열이 준비된다.

 

포인터 배열을 사용해서 문자 데이터를 초기화하려면

      static char *name[ ]={
              "Hyolee",
              "Joohyun",
              "Yoori"
     };

 

라고 표시한다.

 

포인터 배열을 사용해서 문자열의 각 문자를 참조하고자 하는 경우에는 name[0][2] 등과 같이 표시한다. 이것으로 문자열 "Hyolee"의 세 번째 문자인 'o'가 참조가 된다.

 

예제 4. 포인터 배열을 사용해서 저장된 문자열 데이터 중 각 데이터의 첫번째 문자가 H인 것만을 표시하여라.
   
● 프로그램
#include <stdio.h>
#define N 4
main()
{
    char *name[]={"Hyolee","Joohyun","Hyori","Yoori"};
    int i;
    for (i=0; i<N; i++)
   {
       if (name[i][0]=='H')   
              printf("%s\n",name[i]);  
   }
}
  

● 해설

선두 문자는 name[i][0]이고 문자열의 선두 주소는 name[i]이다.

 

●결과

Hyolee

Hyori

 

 

5. 포인터의 주소 계산

지금까지는 pa++나 pb++에 의하여 다음 데이터를 참조할 수 있다고 했다. 그러나 int형 데이터가 2바이트 또는 4바이트라는 것을 생각하면 pa++에 의해 pa가 +1씩 증가된 것으로 다음의 데이터를 참조할 수는 없다. 따라서 pa++이 pa의 산술적인 1의 증가를 의미하는 것은 아니다. 실은 C에 있어서 포인터 변수의 연산(pa++나 pa=pa+1)에는 포인터형에 의해서 정해진 α가 더해진다.
     

pa = pa + α

 

α는 일반적으로 char형이면 1, short int형이면 2, long int형이면 4, float형이면 4, double형이면 8이 된다.

 

확인문제 3

/* 포인터에 적용되는 6가지 기본 연산의 예 */
#include <stdio.h>
#define SIZE 4
void main(void)
{
      int array[SIZE] = {15, 20, 25, 30}, *pt1, *pt2;

      pt1 = array;
      pt2 = &array[2];

      printf("&array[0] = %u, pt1 = %u, *pt1 = %d, &pt1 = %u\n",
            array, pt1, *pt1, &pt1);
      pt1++;
      printf("&array[1] = %u, pt1 = %u, *pt1 = %d, &pt1 = %u\n",
            &array[1], pt1, *pt1, &pt1);

      printf("&array[2] = %u, pt2 = %u, *pt2 = %d, &pt2 = %u\n",
            &array[2], pt2, *pt2, &pt2);
      pt2++;
      pt1--;
      printf("&array[3] = %u, pt2 = %u, *pt2 = %d, &pt2 = %u\n",
            &array[3], pt2, *pt2, &pt2);

      printf("Relative position: pt2 - pt1 = %u elements\n",
              pt2 - pt1);
      printf("Relative address:  pt2 - pt1 = %u\n",
              (pt2 - pt1) * sizeof(*pt1));

      pt1 = pt2;
      printf("pt1 = %u, pt2 = %u, *pt1 = %d, *pt2 = %d\n",
            pt1, pt2, *pt1, *pt2);
}

 

포인터간의 뺄셈 연산을 수행하려면 반드시 두 개의 포인터가 동일한 자료형의 대상체를 포인트하고 있어야 합니다. 즉, 두 개의 포인터가 동일한 배열의 서로 다른 원소를 가리키고 있을 때 가능합니다. 만약 double형 포인터에서 int형 포인터의 뺄셈을 수행하면 컴파일러는 "Suspicious pointer conversion"이라는 오류 메시지를 출력할 것입니다.

한 배열의 서로 다른 원소를 가리키는 두 개의 포인터간에 뺄셈을 수행하면 두 개의 포인터가 가리키는 원소의 위치차를 계산하게 됩니다. "6-5 포인터의 주소 계산"에서 설명한 것처럼 이는 마치 포인터에 1을 증가시키면 다음 원소로 포인터가 증가하는 것과 동일합니다. 확인 문제 6-3에서 포인터의 상대적 위치(주소)를 계산하는 것을 보았습니다.

 

printf("Relative position: pt2 - pt1 = %u elements\n", pt2 - pt1);

 

즉, 위의 실행문은 포인터 변수 p2가 가리키는 배열의 원소의 위치로부터 포인터 변수 p1이 가리키는 배열의 원소의 의치를 감산하는데, 이 때 결과는 배열의 원소간의 상대적 위치의 차가 됩니다. 따라서 컴푸터 내부에서 배열 원소간의 위치의 차를 계산하기 위해 실제 연산의 수행은 다음과 같이 수행한다고 생각하면 됩니
다.

(pt2 - pt1) / sizeof(배열의 자료형) <=> (8902 - 8896) / 2

 

따라서, 연산의 실행 결과는 3이 됩니다. 즉, 실제 두 포인터에 저장된 주소의 차는 6이 되지만 배열의 자료형이 int형이므로 배열 원소간의 상대적 위치의 차를 계산하기 위해서 컴파일러는 int형의 바이트 크기로 나누게 되는 것입니다.

 

확인문제 4

/* 두 포인터간의 상대적 위치를 이용하여 배열 중앙의 상대적 주소를 계산하는 프로그램 */
#include <stdio.h>
#define SIZE 7
void main(void)
{
      int array[SIZE] = {15, 20, 25, 30, 35, 40, 45};
      int *low, *high, *mid, i;

      low  = array;
      high = &array[SIZE-1];
      for(i = 0; i < SIZE; i++)
        printf("&array[%d] = %u, array[%d] = %d\n",
                i, &array[i], i, array[i]);

      mid = low + ((high - low) / 2);
      printf("low = %u, high = %u, high - low = %u\n",
            low, high, high - low);
      printf("mid = %u, array[%d] = %d\n", mid, (high - low )/ 2, *mid);
}

 

확인문제 5

/* &array[i],  array  + 1,  pt  +  i,  &pt[i]를  이용한 배열  원소의  주소  참조
   방법과 array[i],  *(array +  1), *(pt  + i),  pt[i]의 배열  원소의 참조  방법*/
#include <stdio.h>
#define SIZE 4
void main(void)
{
      int    array[SIZE] = {15, 20, 25, 30}, i, *pt;

      pt = array;

      printf("array[i]    *(array + 1)    pt[i]    *(pt + 1)  \n");
      printf("----------------------------------------------\n");
      for(i = 0; i < SIZE; i++)
          printf("%5d  %12d  %11d %9d\n",
                   array[i], *(array + i), pt[i], *(pt + i));

      putchar('\n');
      printf("&array[i]    array + 1    &pt[i]    pt + 1  \n");
      printf("-------------------------------------------\n");
      for(i = 0; i < SIZE; i++)
          printf("%7u  %10u  %11u %9u\n",
                   &array[i], array + i, &pt[i], pt + i);
}

 

확인문제 6

/*  *pt++, (*pt)++, *++pt, ++*pt의 연산을 확인하는 예제 프로그램  */
#include <stdio.h>
#define SIZE 5
void main(void)
{
      int    array[SIZE] = {10, 15, 20, 25, 30}, data, *pt;

      pt = array;
      printf("--- data = *pt++ Operation ---\n");
      printf("Before => pt = %5u, *pt = %3d\n", pt, *pt);
      data = *pt++;
      printf("After  => pt = %5u, *pt = %3d, data = %3d\n\n", pt, *pt, data);

      printf("--- data = (*pt)++ Operation ---\n");
      printf("Before => pt = %5u, *pt = %3d\n", pt, *pt);
      data = (*pt)++;
      printf("After  => pt = %5u, *pt = %3d, data = %3d\n\n", pt, *pt, data);

      printf("--- data = *++pt Operation ---\n");
      printf("Before => pt = %5u, *pt = %3d\n", pt, *pt);
      data = *++pt;
      printf("After  => pt = %5u, *pt = %3d, data = %3d\n\n", pt, *pt, data);

      printf("--- data = ++*pt Operation ---\n");
      printf("Before => pt = %5u, *pt = %3d\n", pt, *pt);
      data = ++*pt;
      printf("After  => pt = %5u, *pt = %3d, data = %3d\n\n", pt, *pt, data);
}

 

확인문제 7

/* 2차원 배열 array[3][2]의 주소를 확인하는 프로그램 */
#include <stdio.h>
void main(void)
{
      int array[3][2];

      printf("      array = %u,  array[0] = %u, &array[0][0] = %u\n",
            array, array[0], &array[0][0]);
      printf("     *array = %u, &array[0] = %u, &array[0][0] = %u\n",
            array, &array[0], &array[0][0]);
      printf("(array + 1) = %u,  array[1] = %u, &array[1][0] = %u\n\n",
            array + 1, &array[1], &array[1][0]);
      
      printf("(array + 1) = %u,   array[0] + 1 = %u, &array[0][0] + 1 = %u\n",
            array + 1, array[0] + 1, &array[0][0] + 1);
      printf("(array + 1) = %u,  &array[0] + 1 = %u, &array[1][0] + 1 = %u\n",
             array + 1, &array[0] + 1, &array[1][0] + 1);
      putchar('\n');

      printf("      sizeof(array) = %d\n", sizeof(array));
      printf("   sizeof(array[0]) = %d\n", sizeof(array[0]));
      printf("sizeof(array[0][0]) = %d", sizeof(array[0][0]));
}

 

터보 씨에서는 각각 12, 4, 2입니다. 프로그램 전체를 설명하지요. 확인 문제 6-7의 첫 번째 실행 결과는 다음과 같다고 가정합시다. 항상 일정하지는 않겠지요?

 

array = 65480, array[0] = 65480, &array[0][0] = 65480

 

위의 결과에서 보듯이 array, array[0], &array[0][0]은 모두 배열 array의 시작 주소인 65480을 갖는다는 것을 알 수 있습니다. 즉, 배열명 array는 부분 배열의 시작 주소인 &array[0]을 갖게 되며, 부분 배열 array[0]은 자신의 첫 번째 원소의 시작 주소 &array[0][0]을 갖게 되므로 모두 동일하다는 것입니다. 두 번째 출력도 유사하게 *array는 &array[0]의 주소를 갖게 되므로 역시 65480 번지를 갖게 됩니다.

 

(array+1) = 65480, array[1] = 65480, &array[1][0] = 65480

 

위의 세 번째 출력에서 array + 1은 배열의 시작 주소(&array[0])에 1을 더하게 되므로 siaeof(array[0])의 크기만큼 증가하게 됩니다. 따라서 4만큼 증가한 65484 번지가 됨을 알 수 있습니다.

 

(array+1) = 65484, array[0]+1 = 65482, &array[0][0]+1 = 65482

(array+1) = 65484, &array[0]+1 = 65484, &array[0][0]+1 = 65486

 

위의 네 번째 출력과 다섯 번째 출력에서 array[0] + 1은 65482 번지를 가리키고, &array[0] + 1이 65484 번지를 가리킨다는 것을 알 수 있습니다. 먼저 네 번째 출력의 array[0] + 1을 살펴보면 array[0]은 &array[0][0]과 동일합니다. 따라서 array[0] + 1은 &array[0][0] + 1과 동일하므로 &array[0][1]이 됩니다. 그러나 다섯 번째 출력에서 &array[0]은 부분 배열 array[0]의 시작 주소이므로 array와 동일하기 때문에 &array[0] + 1은 array + 1과 동일하므로 65484 번지가 됩니다.

 

결론적으로 array와 &array[0]는 서로 동일하며, 1만큼 증가시켰을 때(array + 1, &array[0] + 1) 부분 배열의 크기 만큼 번지가 증가하게 되고, *array, array[0], &array[0][0]은 서로 동일하며 1만큼 증가시켰을 때(*array + 1, array[0] + 1, &array[0][0] + 1) 최하위 배열의 원소의 크기인 int 형 크기만큼 주소를 증가시킵니다. 이를 확인하는 출력이 다음의 마지막 세 줄로 출력되는 sizeof 연산자입니다.

 

sizeof(array) = 12

sizeof(array[0]) = 4

sizeof(array[0][0]) = 2

 

위의 결과에서 보는 것 처럼 배열 array는 전체 배열을 구성하고 있는 원소의 바이트 수인 12(터보 씨에서)를 출력하게 되며, 부분 배열 array[0]는 부분 배열에 포함된 원소들의 바이트 수인 4(터보 씨에서)를 출력하게 됩니다. 그리고 마지막으로 array[0][0]는 int 형 원소를 표현하기 위한 바이트 수인 2(터보 씨에서)가 됩니다.

 

확인문제 8

/* 포인터와 간접 연산자, 배열의 첨자를 이용하여 배열의 특정 원소를 접근하는 프로그램 */
#include <stdio.h>
void main(void)
{
      int array[3][2] = {{10, 20}, {100, 200},{1000, 2000}};

      printf("array[0][0] = %d,   *(array[0]) = %d,     *(*(array)) = %d\n",
            array[0][0], *(array[0]), *(*(array + 0)));
      printf("array[0][1] = %d,   *(array[0]+1) = %d,   *(*(array)+1) = %d\n\n",
            array[0][1], *(array[0] + 1), *(*(array + 0) + 1));

      printf("array[1][0] = %d,  *(array[1]) = %d,    *(*(array+1)) = %d\n",
            array[1][0], *(array[1]), *(*(array + 1)));
      printf("array[1][1] = %d,  *(array[1]+1) = %d,  *(*(array+1)+1) = %d\n\n",
            array[1][1], *(array[1] + 1), *(*(array + 1) + 1));

      printf("array[2][0] = %d, *(array[2]) = %d,   *(*(array+2)) = %d\n",
            array[2][0], *(array[2]), *(*(array + 2)));
      printf("array[2][1] = %d, *(array[2]+1) = %d, *(*(array+2)+1) = %d\n",
            array[2][1], *(array[2]+1), *(*(array+2)+1));
}

 

확인문제 9

/*  *(*(array + i) + j)의 수식을  확인하기 위한 프로그램 */
#include <stdio.h>
void main(void)
{
      int array[3][2] = {{10, 20}, {100, 200},{1000, 2000}};
      int i, j;
      i = 2, j = 1;

      printf("          array + i = %u,         &array[i] = %u\n", array + i, &array[i]);
      printf("       *(array + i) = %u,      &array[i][0] = %u,      array[i] = %u\n",
            *(array + i), &array[i][0], array[i]);
      printf("     *(array+i) + j = %u,  &array[i][0] + j = %u,  &array[i][j] = %u\n",
            *(array + i) + j, &array[i][0] + j, &array[i][j]);
      printf("*(*(array + i) + j) = %d,        array[i][j] = %d\n",
            *(*(array + i) + j), array[i][j]);
}

 

확인문제 10

/* 정방형 2차원 배열의 원소에 저장된 데이타의 이동 */
#include <stdio.h>
#define SIZE 2
void main(void)
{
     int A[5][5];
     int B[5][5], i, j, m, n;
     int p, q, x, z, value;

     x = n = z = value = 1;
     m = SIZE;
     p = 1;
     q = 0;
     /* 2차원 배열 A에 초기값 할당 */
     for( i = 0; i <= SIZE; i++)
         for( j = 0; j <= SIZE; j++)
            *(*(A + i) +j) = value++;

     for(i = 0; i <= SIZE; i++)
     {
         for(j = 0; j <= SIZE && p >= 0 && n <= SIZE; j++)
         {
           if( x <= SIZE ) /* 배열의 상위 부분 처리 */
           {
                if(i == 0 && j == 0)
                {
                     B[i][j] = A[i][j];
                     continue;
                }
                *(B[p] + q) = *(A[i] +j);
                p--;
                q++;
                if( p < 0 && j <= SIZE)
                {
                     p = ++x;
                     q = 0;
                }
           }
           else /* 배열의 하위 부분 처리 */
           {
               B[m][n] = A[i][j];
               n++;
               m--;
               if( n > SIZE )
               {
                   n = ++z;
                   m = SIZE;
               }
           }
         }
         if( i == SIZE && j == SIZE) (*(B + i))[j] = (*(A + i))[j];
     }

     /* 이동 결과의 출력 */
     for(p = 0; p <= SIZE; p++)
     {
         for(q = 0; q <= SIZE; q++)
           printf("%-5d", B[p][q]);
         putchar('\n');
         putchar('\n');
     }
}

 

확인문제 11

/* array, &array[0]와 array[0], &array[0][0]의 차이 */
#include <stdio.h>
void main(void)
{
      int array[3][5] = {{1, 2, 3, 4, 5}, {6, 7, 8, 9, 10},
                       {11, 12, 13, 14, 15}};
      clrscr( );

      printf("array = %u, &array[0] = %u, array[0] = %u, &array[0][0] = %u\n",
            array, &array[0], array[0], &array[0][0]);
      printf("array + 1 = %u, &array[0] + 1 = %u, array[1] = %u\n",
            array + 1, &array[0] + 1, array[1]);
      printf("array[0] + 1 = %u, &array[0][0] + 1 = %u, &array[0][1] = %u\n",
            array[0] + 1, &array[0][0] + 1, &array[0][1]);
      printf("sizeof(array[0]) = %d bytes.\n", sizeof(array[0]));
      printf("sizeof(array[0][0]) = %d bytes.\n", sizeof(array[0][0]));
}

 

확인문제 12

/* 2차원 배열 포인터 pt의 연산을 확인하는 예제 */
#include <stdio.h>
void main(void)
{
      double table[3][2];
      double (*pt)[2];

      pt = table;
      printf("    pt = %u,       *pt = %u, pt[0] = %u, &table[0][0] = %u\n",
            pt, *pt, pt[0], &table[0][0]);
      printf("pt + 1 = %u, *(pt + 1) = %u, pt[1] = %u, &table[1][0] = %u\n",
            pt + 1, *(pt + 1), pt[1], &table[1][0]);
}

 

예제 5. 포인터 배열로 표시되는 문자 패턴으로 반복되는 모양을 작성하여라.

● 프로그램

#include <stdio.h>

#include <string.h>

main( )

{

         char *finkl[ ]={"[              ]",   

                               "[              ]",

                               "[  Hyolee !!   ]",

                               "[              ]",

                               "[              ]" };

                 int j, k;

                 for (j=0; j<=14; j++){

         for (k=1; k<=3; k++){  

                 printf("%s", finkl[j%5]);

             }  

            printf("\n") ;

        }  

}

 

● 해설     

j가 0~14까지 변화하는 동안 j%5는 0, 1, 2, 3, 4, 0, 1, 2...로 반복하여 값을 표시한다.

 

● 결과

   [                  ][                  ][                 ]

   [                  ][                  ][                 ]

   [  Hyolee !!   ][  Hyolee !!   ][  Hyolee !!   ]

   [                  ][                  ][                 ]

   [                  ][                  ][                 ]

   [                  ][                  ][                 ]

   [                  ][                  ][                 ]

   [  Hyolee !!   ][  Hyolee !!   ][   Hyolee !!  ]

   [                  ][                  ][                 ]

   [                  ][                  ][                 ]

   [                  ][                  ][                 ]

   [                  ][                  ][                 ]

   [   Hyolee !!  ][   Hyolee !!  ][  Hyolee !!   ]

   [                  ][                  ][                 ]

   [                  ][                  ][                 ]

 

 

 

그리드형

댓글