이전 글에서 포인터에 대해 알아보았다.
이제 포인터와 배열의 관계에 대해 알아볼 것이다.
배열의 이름도 사실 포인터이다. 하지만 그 값을 바꿀 수 없는 "상수 형태의 포인터"이다.
포인터 변수와 배열의 이름을 비교해보면 다음과 같다.
표에서 보이듯 둘 다 이름이 존재하며, 특정 메모리 공간의 주소 값을 지닌다는 공통점이 있지만 포인터 변수는 이름이 의미하듯이 변수이고, 배열의 이름은 가리키는 대상의 변경이 불가능한 상수라는 차이가 있다. 배열은 상수 형태의 포인터이며 "포인터 상수"라고도 부른다.
또한 배열의 이름도 포인터이기 때문에 배열의 이름을 피연산자로 하는 *연산이 가능하다.
배열이름의 포인터 형과 배열이름을 대상으로 하는 *연산
int arr[5];
이와 같이 배열이 선언돼 있자 하자.
배열의 이름 arr이 가리키는 것은 배열의 첫 번째 요소이다. 이 첫 번째 요소가 int형 변수이기 때문에 arr은 int형 포인터라는 것을 알 수 있다.
* 배열이름의 포인터 형은 배열의 이름이 가리키는 대상을 기준으로 결정한다.
#include<stdio.h>
int main() {
int arr[3] = {1, 2, 3};
*arr += 100;
printf("%d %d %d", arr[0], arr[1], arr[2]);
return 0;
}
// 101 2 3
배열의 이름은 첫 번째 요소를 가리키기 때문에 첫 번째 요소의 값에만 증감이 적용된다.
포인터 변수와 배열의 이름은 서로 변수냐 상수냐의 차이가 있을 뿐 둘 다 포인터이다. 따라서 포인터 변수로 할 수 있는 연산은 배열의 이름으로도 할 수 있고, 그 반대의 경우도 가능하다.
#include<stdio.h>
int main() {
int arr[3] = {1, 2, 3};
int * ptr = arr;
for(int i=0; i<3; i++) {
printf("%d %d\n", ptr[i], arr[i]);
}
return 0;
}
/*
1 1
2 2
3 3
*/
포인터 연산
앞에서 이미 ++, --, +=, -= 등의 연산을 봤었을 것이다. 하지만 포인터에서 이러한 연산을 하면 예상과는 다른 결과가 나올 것이다.
#include<stdio.h>
int main() {
int *ptr1 = 0x0010;
double *ptr2 = 0x0010;
printf("%p %p\n", ptr1+1, ptr1+2);
printf("%p %p\n", ptr2+1, ptr2+2);
printf("%p %p\n", ptr1, ptr1);
ptr1++;
ptr2++;
printf("%p %p", ptr1, ptr2);
return 0;
}
/*
00000014 00000018
00000018 00000020
00000010 00000010
00000014 00000018
*/
결과를 보면 예측할 수 있듯이 int형 포인터를 대상으로 1을 증가시키면 4가 증가하고, double형 포인터를 대상으로 1을 증가시키면 8이 증가된다.
일반화를 해보자면,
"TYPE형 포인터를 대상으로 n의 크기만큼 값을 증감하면, n * sizeof(TYPE)의 크기만큼 주소 값이 증감한다."
#include<stdio.h>
int main() {
int arr[3] = {1, 2, 3};
int *ptr = arr;
printf("%d %d %d\n", *ptr, *(ptr+1), *(ptr+2));
printf("%d ", *ptr);
ptr++;
printf("%d ", *ptr);
ptr++;
printf("%d ", *ptr);
ptr--;
printf("%d ", *ptr);
ptr--;
printf("%d ", *ptr);
return 0;
}
/*
1 2 3
1 2 3 2 1
*/
증감 연산을 통해 주소 값을 증감하면서, 이러한 형태의 배열 접근이 가능하다.
상수 형태의 문자열을 가리키는 포인터
마지막에 널(NULL) 문자가 삽입되는 문자열은 배열을 이용해 선언하는 방식과, char형 포인터 변수를 이용하는 방식이 있다.
- char str1[] = "My String";
- 배열의 길이는 자동으로 계산된다.
- 배열을 기반으로 하는 "변수 형태의 문자열 선언"이다.
- char *str2 = "Your String";
- 메모리 공간에 문자열이 저장된다.
- 문자열의 첫 번째 문자 Y의 주소 값이 반환되고, 반환 값이 포인터 변수 str2에 저장된다
배열이름 str1은 계속해서 각 단어가 저장된 위치를 가리키는 상태여야 하지만, 포인터 변수 str2는 다른 위치를 가리킬 수 있다.
또한 배열이름 str1과 같이 선언이 되면 문자열은 배열에 저장이 되게 되는데, 배열을 대상으로는 값의 변경이 가능하므로 "변수 형태의 문자열"이라 하고, 포인터 변수 str2와 같이 선언이 되면 "상수 형태의 문자열"이라고 한다.
포인터 변수로 이루어진 배열(포인터 배열)
포인터 변수로 이뤄져 주소 값의 저장이 가능한 배열을 "포인터 배열"이라 한다.
포인터 배열의 선언 방식은 다음과 같다.
int *arr1[3];
double *arr2[5];
기본 자료형 배열의 선언방식과 동일하게 배열의 이름 앞에 배열 요소의 자료형 정보를 선언하면 된다.
#include<stdio.h>
int main() {
int n1 = 10, n2 = 20, n3 = 30;
int *arr[3] = {&n1, &n2, &n3};
for(int i=0; i<3; i++) {
printf("%d ", *arr[i]);
}
return 0;
}
// 10, 20, 30
문자열을 저장하는 포인터 배열
문자열 배열을 보자. 이는 문자열 주소 값을 저장할 수 있는 배열이다. 사실상 char형 포인터 배열인 것이다.
char *str[3];
문자열의 주소 값을 저장할 수 있는 배열이다 보니 문자열 배열이라 불리는 것이다.
#include<stdio.h>
int main() {
char *str[3] = {"Simple", "String", "Array"};
for(int i=0; i<3; i++) {
printf("%s ", str[i]);
}
return 0;
}
// Simple String Array
'Programming > C' 카테고리의 다른 글
C언어 - 다차원 배열 (1) | 2021.10.02 |
---|---|
C언어 - 포인터와 함수에 대한 이해 (4) | 2021.09.30 |
C언어 - 포인터의 이해 (0) | 2021.09.27 |
C언어 - 1차원 배열 (0) | 2021.09.23 |
C언어 - 지역변수와 전역변수 (0) | 2021.09.22 |
댓글