1. 포인터 배열(Array of Pointer)이란 ? 

   = 요소가 포인터로 이루어진 배열 


ex) 크기 5인 정수형 포인터 배열 


 int *arpi[5];     // 정수형 포인터 5개가 모여 이루어진 배열



#include<stdio.h>

#include<stdlib.h>

 

void main(void)

{

     char *arps[]={"고양이","개","오랑우탄","돼지","지렁이"};

     int i;

 

     for (i=0;i<5;i++) {

          printf("%s\n",arps[i]);

     }

} 

 



  각 문자열들은 메모리상에 흩어져서 존재

  위치에 상관없이 이 문자열들에 대한 반복처리 가능

  이렇게 만들어진 문자열 배열을 [ Ragged 배열 ] 이라 한다.

  (장점 : 개별 문자열의 길이가 달라도 낭비되는 메모리가 없다)




2. 포인터 배열의 활용


 3개 학급의 성적 처리를 할 수 있는 예제


#include<stdio.h>

#include<stdlib.h>

 

void main(void)

{

     int *ar[3];

     int num[3];

     int i;

 

     for (i=0;i<3;i++) {

          printf("%d반의 학생수를 입력하세요 : ",i+1);

          scanf("%d",&num[i]);

          ar[i]=(int *)malloc(num[i]*sizeof(int));

     }

 

     // 여기서 성적을 처리한다.

 

     for (i=0;i<3;i++) {

          free(ar[i]);

     }

} 

 

   1. 각 학급의 학생수를 입력받아 num배열에 저장

   2. 입력된 수 만큼 메모리 동적할당

   3. ar배열에 차례로 그 번지를 저장

   4. ar배열이 가리키는 포인터들은 각 학급 학생들의 성적을 저장할 수 있는 

      배열이 됨 .

   5. 성적 처리가 끝난 후 free함수로 해제


   이렇게 만들어진 ar배열은 2차 정수형 배열같이 사용 가능


   ex) ar[0][1]  -> 0번째 학급의 1번째 학생의 성적



3. 포인터와 배열

   

   int ar[n];      -> ar[n]은 n개의 정수형 변수를 모아놓은 배열이다. 여기서 n은 변수가 아니라 상수.  sizeof(ar) = 20  

int *pi         ->  pi는 정수형 변수 하나의 위치를 가리킬 수 있는 포인터



But! 동적 메모리 할당으로 pi로도 정수형 배열을 가리킬 수 있다


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


그림으로 도식화해보면


pi는 ar과 똑같은 자격을 가지며 정수형 배열처럼 행세할 수 있다. 

pi나 ar이나 둘 다 배열의 선두 번지를 가리키며 ar[2]로 2번째 요소를 읽을 수 있듯이 pi[2]로도 2번째 요소를 읽을 수 있다. 

    

그렇다면 다른점은?


① 포인터 = 변수  vs  배열 = 상수이다

   pi는 고유의 메모리를 차지하고 있고 언제든지 다른 대상을 가리킬 수 있지만 ar은 선언할 때 그 위치가 이미 고정되므로 다른 대상을 

   가리킬 수 없다. ar로는 오로지 배열의 선두 번지를 읽을 수 있을 뿐이며 이 선두 번지를 기준으로 하여 배열 요소를 읽는다.

② pi가 가리키는 배열의 크기는 동적으로 결정가능  vs   ar이 가리키는 배열의 크기는 선언할 때 정적으로 결정

   고정된 길이의 배열이 필요하면 int ar[n]; 선언문으로 배열을 생성하는 것이 편리하고 가변 길이의 배열이 필요하면 int *형의 포인터 변수를 

   선언한 후 malloc으로 할당해서 사용해야 한다. 포인터로 할당한 배열은 실행중에라도 realloc으로 크기를 재할당하여 변경할 수 있다.

③ 배열은 그 자체가 크기 때문에 함수의 인수로 전달할 수 없다  vs  포인터는 대상체가 무엇이든간에 4바이트의 크기밖에 차지하지 않으므로 함수로 전달할 수 있다

   그래서 배열을 함수로 전달할 때는 반드시 포인터를 사용해야 한다.

④ 배열로 요소를 읽는 것과 포인터로 대상체를 읽는 동작은 속도 차이가 있다

   배열의 첨자 연산은 매번 배열 선두에서부터 출발하지만 포인터는 대상체로 직접 이동해서 바로 읽으므로 액세스 속도가 빠르다. *pi는 pi가 가리키는 곳을 바로 읽지만 ar[n]은 *(ar+n)으로 일단 번지를 더한 후 읽어야 하므로 조금 느리다. 대단한 속도 차이는 아니지만 대규모의 반복적인 루프에서는 이 속도차도 결코 무시 못할 정도로 크다. 대략 포인터가 배열보다 두 배 정도 빠르다.




심화


I. int *api[5];    -> 위에서 살펴봤던 포인터 배열이다


 포인터배열의 각 요소들은 정수형 변수 or 동적 할당된 정수형 배열을 가리킬 수 있다


  II. int **papi;    -> 위와 같은 api같은 포인터 배열을 가리킬 수 있다.

 




출처 : www.soen.kr


'Engineering > Data Sturcture' 카테고리의 다른 글

포인터공부7 - 구조체  (0) 2016.03.19
포인터공부4 - 이중포인터  (0) 2016.03.16
포인터공부3 - 동적메모리 할당  (0) 2016.03.15
포인터공부 2  (0) 2016.03.15
포인터공부 1  (0) 2016.03.15

1. 동적할당의 필요성


 필요 데이터 크기만큼 메모리를 할당하고 싶은데 정적 할당으로는 이를 만족시킬 수 없다.


int stNum;

printf("학생수를 입력해 주세요. ");

scanf("%d",&stNum);

int arScore[stNum];


 위와 같은 코드에서 배열선언문의 크기값에 변수가 들어갈 수 없기 때문에 학생 수에 맞춰 배열을 할당할 수 없다


* 동적 할당된 메모리는 이름이 없는 변수라고 할 수 있다

  독점적인 메모리 영역을 차지하고 있으므로 일단 값을 기억할 수 있지만 이름이 없으므로 오로지 포인터로만 접근할 수 있다. 

  그래서 malloc 함수가 리턴하는 포인터는 반드시 적절한 타입의 포인터 변수로 대입받아야 한다. 

  시작 번지를 잃어버리면 할당된 메모리를 쓸 수 없음은 물론이고 다 사용하고 난 후에 해제하지도 못한다.




2. 할당 및 해제

 

   메모리를 동적으로 할당 및 해제할 때에는 두가지 함수를 사용한다.


void *malloc(size_t size );

void free(void *memblock );

 

1) malloc (엠얼록) : 인수로 필요한 메모리양을 바이트 단위로 전달하면 요청한만큼 할당한다.


  ex) 10바이트가 필요하면 malloc(10)이라고 호출

     1000바이트가 필요하면 malloc(1000)이라고 호출

     = 실행중에 할당하는 것이므로 malloc(Num)과 같이 변수도 사용할 수 있다. malloc은 응용 프로그램이 필요로하는 양만큼 운영체제에게 할당을 요청하며 

      운영체제는 사용되지 않는 빈 영역(힙)을 찾아 요청한만큼 메모리를 할당하여 그 시작 번지를 리턴한다. 

   (* 응용 프로그램이 할당한 메모리를 어떤 목적에 사용할지는 알 수 없으므로 malloc은 void *형을 리턴하며 받는 쪽에서는 원하는 타입으로 캐스팅해야 한다. )



 2) free 함수 :  동적으로 할당된 메모리를 해제한다. 

     -> 응용 프로그램은 메모리를 다 사용한 후에 반드시 free 함수를 호출하여 메모리를 해제해야 한다. 그래야 이 영역이 다른 프로그램을 위해 재활용될 수 있다. 다음 코드는 정수형 변수 10개를 담을 수 있는 메모리를 할당하는 예이다.



        #include <Turboc.h>

 

void main()

{

     int *arScore;

     int i,stNum;

     int sum;

 

     printf("학생수를 입력하세요 : ");

     scanf("%d",&stNum);

     arScore=(int *)malloc(stNum*sizeof(int));

     if (arScore == NULL) {

          printf("메모리가 부족합니다.\n");

          exit(0);

     }

 

     for (i=0;i<stNum;i++) {

          printf("%d번 학생의 성적을 입력하세요 : ",i+1);

          scanf("%d",&arScore[i]);

     }

 

     sum=0;

     for (i=0;i<stNum;i++) {

          sum+=arScore[i];

     }

 

     printf("\n총점은 %d점이고 평균은 %d점입니다.\n",

          sum,sum/stNum);

     free(arScore);

}

  1. stNum에 학생 수를 입력받고

  2. malloc 함수를 통해 arScores는 학생 수에 맞는 정수배열로 동적 할당됨

  3. arScore[i] 연산식으로 성적을 입력받을 수 있다

  4. free 함수를 통해 동적할당 해제



  [참고사항]

  1. malloc 함수는 할당에 실패하면 NULL을 리턴

  2. 만약 할당 해제를 하지 않으면 메모리관리 원칙 상 이 메모리는 시스템을 

     재부팅하기 전까지는 다른 응용 프로그램이 사용 불가




3. 재할당

 

 1) calloc (씨얼록) : 메모리를 할당하되 malloc과 방식이 좀 다르다


   void *calloc( size_t num, size_t size );    => "몇 바이트짜리 몇 개 할당해주세요"


   두 호출문의 결과는 동일하다

ar=(int *)malloc(10*sizeof(int));

ar=(int *)calloc(10,sizeof(int));


calloc을 사용하면 좀 더 논리적으로 표현한다는 점


다른점 : 할당 후 전부 0으로 초기화  (malloc은 쓰레기값이 들어있음)



 2) realloc (리얼록) : 이미 할당된 메모리의 크기를 바꾸어 재할당하는 함수


    void *realloc( void *memblock, size_t size );


 예를 들어 ar 배열이 20바이트만큼 할당되어 있는 상태에서 40바이트로 확대 재할당한다고 해 보자. 만약 ar뒤쪽의 메모리가 비어 있다면 ar을 40바이트로 늘리기만 하면 되므로 ar은 그 자리에서 길이만 늘어난다. 그러나 ar다음의 메모리가 비어 있지 않다면 ar의 위치가 바뀌게 될 것이다.



 재할당이 필요한 예)


 [ 네트워크를 통해 파일을 전송하는 프로그램을 작성할 경우 ]

 전송이 끝나기 전까지는 전송받는 파일의 크기를 정확히 알 수 없다 = 최초 적당한 크기로 버퍼를 할당

 그러다가 최초 할당 버퍼 크기보다 더 많은 데이터가 오면 재할당을 한다




'Engineering > Data Sturcture' 카테고리의 다른 글

포인터공부5 - 포인터배열  (0) 2016.03.16
포인터공부4 - 이중포인터  (0) 2016.03.16
포인터공부 2  (0) 2016.03.15
포인터공부 1  (0) 2016.03.15
2. What is the running time ?  (0) 2016.03.09

+ Recent posts