상수포인터
const 키워드를 붙여 데이터를 상수(바뀌지 않은 값)로 만들 수 있다.
포인터 또한, const 키워드를 붙여 상수로 만들 수 있다.
예를 들어보며 이해해보자
#include <stdio.h>
int main() {
int a;
int b;
const int* p = &a;
p = &b; //올바른 문장
*p = 3; //올바르지 않은 문장
return 0;
}
다음 예시는 컴파일을 했을 경우 에러가 난다.
const int* 의 의미는 const int 형 변수를 가리킨다는 의미가 아니고, int 형 변수를 가리키는데 그 값을 절대로 바꾸지 말라는 의미이다. 즉, p는 int형 변수를 가리키고 있고 const가 붙어있어서 p가 가리키는 변수의 값이 바뀌면 안되는 것이다.
여기서 변수 a는 값이 자유롭게 바뀔 수 있다. 그러나 p를 통해서 변수 a를 간접적으로 가리킬 때는 const로 인해서 값을 바꿀 수 없다.
그래서 *p = 3 ; 이라는 문장에서 오류가 발생한다.
그렇다면, p = &b;의 문장은 오류가 나지 않는다. 그 이유를 다음의 예와 함께 설명해보겠다.
#include <stdio.h>
int main() {
int a;
int b;
int* const p = &a;
p = &b; //올바르지 않은 문장
*p = 3; //올바른 문장
return 0;
}
다음의 예제도 컴파일을 하면 앞의 예와 같은 오류가 발생한다.
int* p = &a ; 만 보았을 때 int 형을 가진 p의 포인터를 a의 주소값으로 정의했다. 이번에는 int* 와 p 사이에 const를 통하여 p의 값이 바뀌면 안되는 값으로 정의하였다.
즉, p에 a의 주소값이 저장되고 const를 통하여 p의 값이 바뀔 수 없으므로, p는 a를 가리키고 있다는 것이 바뀔 수 없다.
그래서 p = &b; 라는 문장이 오류가 발생하는 것이다.
포인터의 덧셈
다음으로 포인터의 덧셈과 뺄셈에 대해서 공부해보자.
먼저 int 형의 변수를 가리키고 있는 포인터에 1을 더해보자
#include <stdio.h>
int main() {
int a;
int *p = &a;
printf("p의 값 : %p", p);
printf("p + 1 의 값 : %p", p + 1);
return 0;
}
결과를 확인해보면 두 수의 차이는 4인 것을 확인할 수 있다. (16진수인 것에 유의!)
왜 이러한 결과가 나오냐면 int 형이 4 바이트를 가지고 있기 때문입니다.
다른 예도 같이 들어보겠습니다.
#include <stdio.h>
int main() {
int a;
double b;
char c;
int *p = &a;
double* pp = &b;
char* ppp = &c;
printf("p의 값 : %p \n", p);
printf("p + 1 의 값 : %p \n", p + 1);
printf("pp의 값 : %p \n", pp);
printf("pp + 1 의 값 : %p \n", pp + 1);
printf("ppp의 값 : %p \n", ppp);
printf("ppp + 1 의 값 : %p \n", ppp + 1);
return 0;
}
double 형의 주소값을 가진 b의 경우 +1을 한 경우 8이 차이나고,
char 형의 주소값을 가진 c의 경우 +1을 한 경우 1이 차이가 난 것을 확인할 수 있다.
뺄셈 역시 다음과 같은 원리로 결과가 나온다.
포인터와 배열
다음과 같이 배열을 정의해보자.
int arr[5] = {1, 2, 3, 4, 5};
그러면 arr라는 배열은 메모리 상에서 다음과 같이 나타난다.
int형 배열 하나의 원소는 4 바이트를 차지한다.
예를 통하여 int 배열의 각 원소가 4 바이트를 차지하는지 주소값을 통하여 알아보자
#include <stdio.h>
int main() {
int arr[] = { 1,2,3,4,5 };
for (int i = 0; i < 5; i++) {
printf("arrr[%d] 의 주소값 : %p \n",i, &arr[i]);
}
return 0;
}
이처럼 4씩 증가하는 것을 확인할 수 있다. 그러면 배열의 시작 주소값을 담고있는 배열의 포인터를 정의한 다음에 +1을 더하면 그 다음 원소를 가리킨다는 것을 예측할 수 있다.
왜냐하면 자신의 가리키는 데이터 타입의 크기를 곱한 만큼 덧셈이 수행되기 때문이다.
다음의 예를 통하여 확인해보자.
#include <stdio.h>
int main() {
int arr[] = { 1,2,3,4,5 };
int* p;
p = &arr[0];
for (int i = 0; i < 5; i++) {
printf("arrr[%d] 의 값 : %d \n", i, *(p+i));
}
return 0;
}
배열의 주소값과 [] 연산자의 역할
배열을 출력하는 예를 들어보자.
#include <stdio.h>
int main() {
int arr[] = { 1,2,3,4,5 };
for (int i = 0; i < 5; i++) {
printf("arrr[%d] 의 값 : %d \n", i, arr[i]);
}
printf("arr : %p", arr);
return 0;
}
다음과 같이 []를 사용하면 배열 안의 값이 나오는 것을 확인할 수 있고, arr 만 확인하는 경우 해당 배열의 첫 번째 원소의 주소값이 출력되는 것을 확인할 수 있다.
이처럼, [] 연산자는 *(arr + i)로 바뀌어서 처리되는 것이다.
'언어 > C' 카테고리의 다른 글
function - 포인터 인자, 배열 인자 (0) | 2022.03.20 |
---|---|
포인터의 이해(3) - 포인터와 1차원, 2차원 배열 / 더블포인터 / 배열의 포인터 / 포인터 배열 (0) | 2022.03.16 |
포인터의 이해(1) - *, & 연산자의 의미 (0) | 2022.03.11 |