본문 바로가기

C programming/[Algorithm problem]

#1-4. 배열과 포인터

728x90
반응형

배열과 포인터의 조합

배열과 포린터는 메모리 주소를 다룬다는 점에서 동시에 출발한다.

다음 코드는 배열과 포인터를 응용한 코드이다.

#include  <stdio.h>
#include  <string.h>
#include  <stdlib.h>
int main(){
    int arr[] = {1,2,3,4,5};
    int *parr = arr;
    int arrNum = sizeof(arr)/sizeof(arr[0]);
    for(int i=0;i<arrNum;i++) printf("%d\n",*(parr+i));
}

이때 코드에서의 arr의 메모리 주소와 parr의 주소, 그리고 arr[0]의 주소가 같다는 것을 알 수 있다.

그렇다면 왜 배열에서 포인터를 사용해야 할까?

 

배열 이름은 포인터처럼 주소 값을 출력하는 형태로는 사용할 수 있지만 배열의 이름 자체로는 어떠한 연산도 할 수 없다. 하지만 배열을 포인터로 변환하여 사용하면 위의 코드와 같이 출력에서 '메모리의 주소'를 연산하여 사용할 수 있다.

 

즉, 메모리의 주소를 정확히 알지 못하여도 포인터의 시작 메모리 주소는 배열의 메모리 주소이므로 포인터의 주소를 이용하여 배열에 접근이 가능하다. 다시 말해 포인터를 통해 배열의 접근이 쉽다는 뜻이다.

배열과 포인터의 차이점

배열과 포린터는 메모리 주소값을 사용할 수 있다는 공통점이 있다.

하지만 배열은 인덱스라는 개념 때문에 시작 메모리 주소가 고정되지만 포인터는 직접 연산 처리를 할 수 있으므로 포인터가 가리키는 시작 메모리 주소가 이동될 수 있다.

 

배열의 시작 메모리는 한번 설정되면 변경되지 않기 때문에 배열의 항목 값을 읽어오기 위해서는 index를 활용해야 한다. 그러나 포인터는 연산을 이용하면 시작 메모리의 주소 또한 바꿀 수 있다.

 

배열과 포인터는 거의 동일한 기능을 제공하나, 포인터를 사용할 때에는 사용자가 배열의 시작 메모리 위치를 수정할 수 있으므로 메모리의 주소를 잘 파악하여 버그가 생기지 않도록 각별히 주의한다.

배열을 사용하는 포인터를 함수의 매개변수로 사용

포인터를 함수의 매개변수로 사용하기 전에 이중 포인터에 대해 알아보자.

#include  <stdio.h>
#include  <string.h>
#include  <stdlib.h>
//함수를 매개변수로 사용하는 포인터

int main(){
    int data[][2] = {{10,20},{30,40},{50,60},{70,80},{90,100}};
    int (*pdata)[2] = data;

    printf("함수 호출 전\n");
    for(int i=0;i<5;i++){
      for(int j=0;j<2;j++) printf("pdata : %p, [%d][%d] : %d\n",pdata,i,j,(*pdata)[j]);
      pdata ++;
      printf("\n");
    }
}

위 코드의 8번째 줄을 보면 다음과 같다.

 int (*pdata)[2] = data;

이는 이중 포인터로, 1차원 배열의 경우 해당 배열의 주소 값만 실제 배열 값을 찾을 수 있는 반면 2차원 배열은 행과 열로 이루어져 있어 배열 크기에 대한 정보가 필요하기 때문에 포인터 선언과 함께 행에 대한 정보를 추가한다.

 

이때 주의할 점은 다음과 같다.

 

1. 이중 배열을 선언할 때에는 열과 행의 정보가 모두 포함돼야 한다.

2. C언어서 작동하는 선언 기호들은 각각의 우선순위를 가지고 있다. 포인터의 선언 및 간접참조 기호 *는 연산자 실행 순서에 있어 []에 밀리기 때문에 코드 작성 간에 괄호를 사용하는 것이다.

3. 이중 포인터 참조에서는 * 연산자를 두 개 써야 한다. 이때 참조하는 것은 그 행의 첫 번째 요소이다.

4. 이중 배열을 포인터로 참조하고 싶다면 12번 줄과 같이 코드를 작성한다.

 12] for(int j=0;j<2;j++) printf("pdata : %p, [%d][%d] : %d\n",pdata,i,j,(*pdata)[j]);

연산자의 우선순위 표를 참고한다.

연산자의 우선 순위

배열을 사용하는 포인터를 매개변수로 하는 코드

#include  <stdio.h>
#include  <string.h>
#include  <stdlib.h>
//함수를 매개변수로 사용하는 포인터

void PrintPrt(int (*pdata)[2]){
     printf("함수 호출 후\n");
     for(int i=0;i<5;i++){ printf("pdata : %p, *pata : %d\n",pdata,**pdata); pdata ++;}
     printf("\n");
}

int main(){
    int data[][2] = {{10,20},{30,40},{50,60},{70,80},{90,100}};
    int (*pdata)[2] = data;

    printf("함수 호출 전\n");
    for(int i=0;i<5;i++){
      for(int j=0;j<2;j++) printf("pdata : %p, [%d][%d] : %d\n",pdata,i,j,(*pdata)[j]);
      pdata ++;
      printf("\n");
    }

    pdata = data;
    PrintPrt(pdata);
}

이때 주목해야 할 코드는 8번 줄이다.

 for(int i=0;i<5;i++){ printf("pdata : %p, *pata : %d\n",pdata,**pdata); pdata ++;}

해당 코드를 보면 참조 연산자를 2개 사용했음을 알 수 있는데, 이는 이중포인터이기 때문이다.

* 연산자를 한번 사용하면 1차 배열에 참조하고 * 연산자를 한번 더 사용함으로써 2차 배열에 접근하는 것이다. 이때 주의할 점은 2차 배열에 접근 시에 2차 배열의 시작 메모리에 접근하는 것이기 때문에 **(pdata)의 값은 2차 배열의 처음 요소값이 나오게 되는 것이다.

 

다시 말해 이중 참조 연산자를 사용하면 2차 배열의 첫 번째 요소값에 접근하고 2차 배열의 다른 요소값에 접근하기 위해서는 1차 배열에 참조하고 인덱스를 변경해야 한다. 이는 위 코드의 18번 줄을 보면 알 수 있다.

for(int j=0;j<2;j++) printf("pdata : %p, [%d][%d] : %d\n",pdata,i,j,(*pdata)[j]);

 

728x90
반응형

'C programming > [Algorithm problem]' 카테고리의 다른 글

#1-6. 포인터의 활용  (0) 2023.09.04
#1-5. 포인터 배열과 포인터 연산  (0) 2023.09.03
#1-3. 포인터  (0) 2023.08.31
#1-2. 배열과 검색  (0) 2023.08.31
#1-1. 자료형의 크기  (0) 2023.08.30
댓글