본문 바로가기
컴퓨터 공학/C, C++

[ C ] 포인터 이해하기9_포인터 배열, 2차원 배열이란? 예제코드로 개념이해하기

by hahehohoo 2020. 7. 20.
반응형

포인터 배열이란? 2차원 배열이란? 예제로 개념이해하기

 

 

■ 포인터 배열 

 

포인터도 변수이기 때문에 포인터를 저장하는 배열도 있습니다. 

그럼 포인터 배열은 어떻게 선언해야 할까요?

 

일반적인 배열은 아래와 같이 선언합니다.

예) int nums[3];   // 자료형 배열명[요소 수]

int형 데이터를 넣을 수 있는 배열이지요. 

 

그럼 포인터를 저장하는 배열은 

데이터형을 포인터형으로 하면 됩니다. 

예) int* num_pointers[3];

int*를 담는 배열입니다. 

 

 

포인터 배열

[ 예제 코드 ]

int nums1[3] = { 11, 22, 33 };
int nums2[1] = { 90 };
int nums3[4] = { 36, 25, 45, 78 };

int* num_pointers[3];
num_pointers[0] = nums1;
num_pointers[1] = nums2;
num_pointers[2] = nums3;

 

[ 코드 설명 ]

배열의 이름은 그 배열의 첫 번째 요소를 가리키는 주소를 의미합니다. 

그래서 num_pointers[0]에 nums1 배열 이름을 이용하여 그대로 대입할 수 있습니다. 

 

위 예제 코드에서 순서대로

num_pointers[0]에 nums1을,

num_pointers[1]에 nums2을,

num_pointers[2]에 nums3을 대입하였습니다.
즉, num_pointers의 배열에는 각 배열의 첫 번째 요소의 주소값이 들어있습니다. 


그래서 num_pointers는 포인터 배열라고 부를 수 있습니다. 

 


■ 포인터 배열 요소 호출 방법

 

그럼 포인터 배열의 모든 요소를 호출하려면 어떻게 코드를 작성해야 할까요?

1차원 배열은 for문을 통해 인덱스를 1씩 증가하여 데이터에 접근하였습니다. 

 

포인터 배열도 비슷한 개념입니다. 이중 for문을 돌 때랑 비슷하죠. 

그럼 포인터 배열의 요소를 호출하는 코드를 보겠습니다. 

 

[ 예제 코드 ]

void print_array(int* const data[]. const size_t size, const size_t length[])
{
	size_t i;
    size_t j;
	const int* p;
    
    for(i=0; i < size; ++i) {
    	p = data[i];
        printf("nums[%d]:", i);
        
        for(j=0; j<lengths[i]; ++j){
        	printf(" %d", p[j];
        }
        printf("\n";
    }
}

 

 

[ 코드 설명 ]

매개변수로 포인터 배열을 넣는다는 것은 배열의 첫 번째 요소의 주소를 전달한다는 뜻입니다. 

배열을 복사하지 않기 때문에 데이터를 복사하는데 시간 낭비할 필요가 없습니다.

그 외의 "포인터의 용도" 에 관해서는 아래 글을 읽어주세요. 

포인터 용도 글 읽으러가기

 

또한 주소를 전달하기 때문에 포인터 변수만으로 배열의 길이를 알 수 없어서 

함수에서 접근하려면 각 내부 배열의 길이를 알려주는 size_t 배열이 필요합니다. 

 

 

 

 

 

자 여기까지 이해하셔야 다음 2차원 배열을 받아들일 준비가 된 것입니다.

 

- 배열의 이름은 배열의 첫 번째 요소의 주소를 의미한다.

- 포인터 배열과 배열 포인터는 다르다.

- 포인터 배열 선언 방법 이해했다.

 

위 3가지에 완전히 동의하신다면 2차원 배열로 넘어가보겠습니다. 


■ 2차원 배열 

 2차원 배열 선언은 아래와 같이 합니다.

 

int matrix[3][5] = {  // 자료형 배열이름[행 갯수][열 갯수]
	{ 1, 2, 3, 4, 5 },
	{ 1, 2, 3, 4, 5 },
	{ 1, 2, 3, 4, 5 }
};
   

 

 

그럼 포인터 개념을 이용하여 호출할 때 이렇게 코드를 작성하면 될까요?

 

 

void do_magic(int* matrix[5])
{
	/* 코드 생략 */
{

int main(void) 
{
  int matrix[3][5] = {  // 자료형 배열이름[행 갯수][열 갯수]
      { 1, 2, 3, 4, 5 },
      { 1, 2, 3, 4, 5 },
      { 1, 2, 3, 4, 5 }
  };
  do_magic(matrix);
}
   

 

위 코드를 실행하면 컴파일 오류가 발생합니다.

2차원 배열은 한 덩어리 메모리라 주소값이 저장된 곳이 없기 때문입니다. 

그래서 다음과 같이 작성해야 합니다. 

 

void do_magic(int matrix[][5], size_t m) //m은 행의 수, 각 행마다 5개 데이터 있음
{
	/* 코드 생략 */
}

 

matrix[몇 번째 줄],[데이터 5개있다] 라는 뜻이라

matrix[1],[] 하면 컴파일러가 '두번째 줄을 호출하려면 몇 바이트를 뛰어넘어야겠구나.'하고  알 수 있습니다. 

위 코드대로 라면 matrix[1],[] 는 배열 시작 주소보다 20바이트(4바이트 * 요소 5개) 뒤를 호출하겠네요. 

 

컴파일할 수 있는 수정된 코드 전체를 보겠습니다. 

행의 수는 모르기 때문에 전달해주어야 합니다. 

 

void do_magic(int matrix[][5], size_t m)
{
	/* 코드 생략 */
{

int main(void) 
{
  int matrix[3][5] = {  // 자료형 배열이름[행 갯수][열 갯수]
      { 1, 2, 3, 4, 5 },
      { 1, 2, 3, 4, 5 },
      { 1, 2, 3, 4, 5 }
  };
  do_magic(matrix, 3);
}
   

 

 

 

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

C언어 문법 총정리

목록 보러가기 

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

 

 

반응형


댓글