본문 바로가기

Development/C++

배열과 포인터(포인터로 배열 다루기 1.)

포인터 챕터를 마치고.... 배열과 포인터라는 챕터로 들어왔네요.

공부해본 결과..약간 개념적인 이해가 필요한 듯 하니

이해가 가지 않으면 여러번 다시 생각해봐야 할 거 같네요.

 

포인터로 배열 다루기

 

배열은 많은 양의 데이터를 효율적으로 처리할 수 있는 훌륭한 자료구조에요.

변수들이 메모리의 연속된 기억공간에 할당되어 있으므로 

반복문을 사용할 수 있다는 것이 큰 장점이였죠?

그러나 덩치가 큰 만큼 쉽게 움직일 수 없다는 단점도 갖고 있어요.

예를 들어, 아래와 같이 초기화된 배열이 있다고 했을때,

 

int ary[5] = [10,20,30,40,50];

 

이 배열의 모든 값을 한번만 출력한다면 반복문을 사용하면되요.

하지만 프로그램 전체에 걸쳐 빈번하게 출력해야 한다면 함수를 만들어 호출하는게 좋겠죠?

 

배열의 값을 모두 출력하는 함수를 만들어 보자면,

 

void ary_prn(int a, int b, int c, int d, int e)

{

printf("%d, %d, %d, %d, %d\n", a, b, c, d, e);

}

 

이렇게 만들 수 있는데,

ary 배열은 배열요소가 5개이므로 매개변수도 역시 5개를 선언해야 해요.

출력문의 변환문자열도 매개변수의 개수만큼 필요하구요.

함수를 호출할 때도 배열요소의 값들을 모두 하나씩 전달인자로 전해줘야 할거구요.

 

ary_prn(ary[0], ary[1], ary[2], ary[3], ary[4]);

 

하지만.....배열요소의 개수가 500개라면..? 하..ㅋㅋ생각하기도 싫네요^^;

이런 문제는 단지 출력하는 것에만 그치지 않아요.

배열에서 특정 값을 찾고나 배열을 정렬하는 문제 등 배열의 값을 처리하는 

모든 함수에 같은 문제가 발생할거에요.

 

이런 문제를 말끔히 해결하기 위해서는 포인터를 사용해야 해요.

일단 배열을 처리하는 함수를 살펴보기 전에 배열과 포인터의 관계를 이해하고

포인터로 배열요소를 참조하는 방법부터 배워볼께요.

 

포인터로 배열요소를 참조하자

 

배열은 같은 형태의 변수가 메모리의 연속된 공간에 할당되어 있죠.

따라서 첫번쨰 기억공간의 위치를 알면 나머지 기억공간들의 위치도

자연스럽게 확인할 수 있어요.

첫번쨰 기억공간의 시작주소값에 기억공간 하나의 크기를 더하면

다음 기억공간의 시작주소값이 될 거에요.

 



각 배열요소의 시작주소값을 알면 참조연산자로 배열요소들을 참조할 수 있으므로,

결국 배열의 모든 기억공간은 첫 번째 배열요소의 시작주소값만 알면 참조할 수 있겠죠?

그러면... 직접 포인터를 사용해서 배열의 첫 번째와 두 번째 기억공간을 참조해 봅시다.

 

#include <stdio.h>

 

int main()

{

int ary[5]={10,20,30,40,50};

int *ap;

 

ap = &ary[0];            //첫번째 배열요소의 포인터를 포인터변수에 저장한다.

printf("첫번째 배열요소의 주소값 : %u\n", ap);      //첫 번째 요소의 주소값 출력

printf("첫번째 배열요소의 값 : %d\n", *ap);         //배열요소에 저장된 값 출력

 

ap = &ary[0]+4;            //두번째 배열요소의 포인터를 구한다.

printf("두번째 배열요소의 주소값 : %u\n", ap);           //두 번째 요소의 위치값 출력

printf("두번째 배열요소의 값 : %d\n", *ap);            //배열요소에 저장된 값 출력

 

return 0;

}

 




출력 결과에서 확인할 수 있듯이 배열의 첫 번째 요소는 

메모리 36번지(편의상 실제 주소값에서 뒷부분 두 자리만 사용할께요)에

위치한 것을 알 수 있어요.

이 포인터를 참조하여 이곳에 저장된 값을 출력한 결과도 초기화 값과 일치하네요.

 

그러나... 두 번째 배열요소를 참조하기 위하여 계산된 주소는 예상외의 결과인데요.

int형 변수의 크기는 4바이트이므로 두 번째 기억공간의 시작주소값은

첫 번째 기억공간의 시작주소값에 4를 더한 40번지가 되어야 마땅할 건데요.

그러나 실제로 계산된 주소값은 52번지이며 이 주소에 저장된 값 또한 두 번째 배열요소의 초기값과 다르네요.

 

 

이 상황은 어떻게 된걸까요..? 이것은 포인터에 정수값을 더하는 연산이

일반 정수연산과 다르게 수행되기 때문이에요.

포인터는 보통의 정수값과 달리 출생의 비밀을 간직하고 있다는데요.

즉, 어떤 자료형으로부터 계산되었는지에 대한 정보를 가지고 있는 겁니다.

이 혈통을 그대로 유지하기 위해서

포인터에 정수값을 더할 때는 포인터가 가리키는 자료형의 크기를 곱해서 더해 주게 되요.

그리고 연산된 결과값 역시 같은 자료형을 가리키는 포인터가 되는 거구요.



따라서 첫 번째 배열요소의 포인터에 4를 더한 것은

다섯번째 배열요소를 가리키는 포인터를 구한 결과가 된 거에요.

이제 첫 번째 배열요소의 포인터에 정수값을 차례로 더하면

각 배열요소를 가리키는 포인터를 구할 수 있을 거에요.




이들 포인터에 참조연산자만 사용ㅎ면 모든 배열요소의 값을 참조할 수 있어요.

반복문과 참조연산자를 사용하여 모든 배열요소의 값을 출력해 볼게요.



 

#include <stdio.h>

 

int main()

{

int ary[5]={10,20,30,40,50};

int i;

 

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

printf("%d\n", *(&ary[0]+i));

}

 

return 0;

}

 
결국 첫 번째 배열요소의 포인터만 알 수 있으면
주소값을 계산하여 모든 배열요소를 쉽게 참조할 수 있는 거에요.

 

배열명은 포인터!

 

C언어는 첫 번째 배열요소의 포인터를 쉽게 사용할 수 있도록 배열명으로 표현하고 있어요.

즉, 배열명은 첫 번째 배열요소를 가리키는 포인터를 이름으로 기호화한 것입니다.




따라서 첫 번째 배열요소의 시작주소값을 별도로 계산할 필요 없이 

배열명을 직접 사용하여 각 배열요소를 참조할 수 있어요.

예를 들어, 배열명으로 네 번째 배열요소를 참조할 때는 다음과 같이 사용하면 될 거에요.

(더하는 정수값이 배열요소의 첨자와 같다는 것을 기억하세요)



이번에는 배열명을 사용하여 모든 배열요소의 값을 출력해볼께요.

 

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

       printf("%d\n", *(ary+i));

}

 

사실상 컴파일러는 배열요소를 참조할 때 항상 배열명으로부터

배열요소의 주소값을 계산하고 참조연산자를 하용하게 되요.

다만 프로그램을 작성할 때는 의미상 이해하기 쉬운 배열표현을 사용할 수 있는 거구요.

 

 

 

 

 

이쯤해서... 연습문제 하나 가볍게 풀고 다음 포스팅에서 마저 살펴볼께요~^^




ary배열을 선언하고 초기화 되어있는 값들의 평균값을 구하라네요.

배열요소를 참조할 때는 배열명에 정수값을 더하는 포인터를 사용하라니깐...

여기서 배열명은 첫 번째 배열요소를 가리키는 포인터니깐

정수값을 더하라는 소리는 첫 번째 배열요소의 주소(포인터)에

정수값을 더해 다음 배열요소들의 주소들을 구하라는 거겠네요.

여기서... 모든 배열요소의 주소들을 구하기 위해서

for문을 사용하여 순차적으로 구하면 됩니다.

아래에 프로그램짠걸 보면서 설명드릴께요.

 

 

#include <stdio.h>

 

int main()

{

double ary[]={1.5, 20.1, 16.4, 2.3, 3.5};

int i;                                //   for문에 사용될 반복제어변수에요

double avg;

double tot=0;                    //합을 구하기 위한 실수형 변수구요. 쓰레기값이 존재할 수 있으니 0으로 초기화~!^^

 

for(i=0; i<5; i++){              // 첫번째 배열요소부터 마지막 다섯번째 배열요소까지 구해야되니깐 0~4까지

tot+= *(ary+i);          // 여기서 배열명 ary는 첫 번째 배열요소를 가리키는 포인터이고 그 뒤에 반복 제어변수로 i를 넣어

}                                   // 다섯개의 배열요소를 모두 더해요

avg=tot/5;                       // 더한 값에 5를 나누어서 평균값을 구해줘요. tot가 실수형이라 5.0이런식으로 안써두되요^^

 

printf("평균값 : %.2lf", avg);

 

return 0;

}


이렇게 포스팅을 마치고

다음 포스팅으로 넘어갈게요.

아직까지도..뭐 그다지 이해하기 어렵진 않네요^^




 

 

https://archive.ph/lQ756

https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=1stwook&logNo=30179823570 

 

배열과 포인터(포인터로 배열 다루기 1.)

포인터 챕터를 마치고.... 배열과 포인터라는 챕터로 들어왔네요. 공부해본 결과..약간 개념적인 이해가 필...

blog.naver.com