나의 IT일지
배열과 포인터 본문
포인터란 변수의 주소를 저장하는 공간 변수로, 변수를 사용할 때 변수의 주소로 접근하기 위해서 사용한다. 즉 , 포인터는 변수이기에, 연산이 가능하며 연산의 공식은 "저장중인 주소값+증감*저장한 변수의 자료형 크기"이다. 이는 배열의 요소의주소를 찾을 때 유용하다.
#include<stdio.h>
void main() {
int arr[3] = { 1,2,3 };
printf("배열주소:%p\n", &arr);
printf("배열명:%p\n", arr);
printf("첫번째 주소:%p\n", &arr[0]);
printf("두번째 주소:%p\n", & arr[1]);
printf("세번째 주소:%p\n", & arr[2]);
}
위의 코드를 보면, 배열주소와 배열명과 첫번째 요소의 주소가 같은 것을 확인 할 수 있다. 즉, 배열명은 포인터와 유사하게 배열주소를 보유하고 있다. 게다가 요소마다 주소가 연속적으로 부여되어 있는 것을 확인할 수 있다. 이는 배열도 포인터를 사용할 수 있다는 뜻이다. 그렇다면 배열은 포인터를 어떻게 활용할까?
배열과 포인터의 관계
포인터의 연산은 변수의 연산과 다르게 포인터에 저장한 주소값에 변수의 자료형의 크기만큼 증감한다. 이는 배열에서 유용하게 사용할 수 있는데, 요소마다 주소가 부여되어 있어서 포인터 연산을 사용하면, 요소들의 주소를 통해 요소의 값을 구할 수 있게 된다.
#include<stdio.h>
void main() {
int a[5] = { 1,2,3,4,5 };
int* ptr;
ptr = a; // ptr = &a[0]
printf("*ptr:%d, a[0]:%d\n", *ptr, a[0]);
ptr++;
printf("*ptr:%d, a[1]:%d\n", *ptr, a[1]);
}
위의 코드를 보면, 배열주소를 포인터에 저장을 하고 연산하는 것을 확인할 수 있다. 정수형 배열의 주소를 가지고 있는 포인터"ptr"에 증가연산자를 통해 연산하는 경우, "ptr"의 값에 +1이 아닌 정수형의 크기인 +4가 되며, 이러한 규칙을 통해 요소의 주소를 구하는 것이다.
#include<stdio.h>
int main() {
int num[3];
printf("&num[0]: %d , num=%d\n", &num[0], num);
printf("&num[1] : %d, num+1:%d\n",&num[1], num + 1);
printf("&num[2] : %d, num+2:%d\n", &num[2], num + 2);
}
배열명을 출력하면 주소값이기에, 포인터를 연산하여 간접참조연산자를 통해 변수의 값을 구하듯이, 배열명의 연산을 통해 요소의 주소를 구하고 간접참조 연산자를 사용하여 요소의 값을 구하는 것이 가능하다.
(배열명 + 증가량), (배열명 - 감소량)
위의 구조는 배열명의 연산구조로, 배열명에 값을 증감하여 요소의 주소를 구하는 구조이다. 이때, 배열명의 증감은 포인터와 똑같이 저장한 주소값에 요소의 자료형의 크기만큼 증감하게 된다.
#include<stdio.h>
void main() {
int a[5] = { 1,2,3,4,5 };
printf("a[0]:%d\n", a[0]);
printf("a:%d\n", *a);
printf("a[1]:%d\n", a[1]);
printf("*(a+1):%d\n", *(a+1));
printf("a[2]:%d\n", a[2]);
printf("*(a+1):%d\n", *(a + 2));
}
위의 소스코드와 결과를 보면, "a[1] == *(a+1)", "a[2]==*(a+2)"인 것을 알 수 있다. 즉, 배열명의 연산은 요소의 주소를 구하는 방법이며, "배열명[요소번호] = *(배열명 + 요소번호)"인 것을 알 수 있다.
이는 포인터를 통해 값을 부를때도 똑같이 "포인터명[증감량] = *(포인터 + 증감량)"로 적용이 된다. 예를 들면 "*ptr"은 "*(ptr+0)"으로 바꿀수 있으며, "ptr[0]"으로 바꿀 수 있다.
배열명 연산을 사용할 때 주의 사항이 존재한다. 포인터의 연산은 덧셈, 뺄셈, 증감연산자으로 연산이 가능하지만, 배열명의 연산은 덧셈과 뺄셈만 가능하고, 증감연산자는 사용이 불가능하다. 배열명은 변수가 아닌 상수이기에, 값을 새롭게 저장하는 대입연산을 내포하는 증감연산자를 사용할 수 없는 것이다.
번외 ) 포인터 간의 연산
포인터는 정수 뿐만 아니라 포인터간의 연산도 가능하며, 이를 통해 두 변수사이의 간격을 구할 수 있고, 두 변수의 위치비교를 할 수 있다.
#include<stdio.h>
void main() {
int a[] = { 1,2,3,4,5 };
int* p1 = &a[0];
int* p2 = &a[3];
printf("p1:%d,p2:%d\n", p1, p2);
if (p1 > p2) {
printf("p1-p2=%d\n", p1 - p2);
printf("p2는 p1보다 앞에 있다");
}
else
{
printf("p2-p1=%d\n", p2 - p1);
printf("p1은 p2보다 앞에 있다.");
}
}
위의 코드를 보면 두 포인터의 뺄셈과 비교를 통해 두 요소의 간격과 위치비교를 하는 것을 알 수 있다. 특히 뺄셈의 결과가 신기한데, 값이 12가 아닌 3인것을 확인 할 수 있다. 이는 두 주소 사이에 정수형 요소 3개가 있다는 것을 알 수 있다. 즉, 뺄셈의 값은 "값의 차/ 요소의 자료형의 크기"라는 것을 알 수 있다.