포인터란?
주소를 저장하는 '변수'
포인터 사용시 장점
- 메모리 주소를 참조하여 다양한 자료형 변수들의 접근과 조작이 용이함
- 메모리 주소를 참조하여 배열과 같은 연속된 데이터에 접근과 조작 용이
- 동적 할당된 메모리 영역에 접근과 조작 용이
포인터 변수의 선언과 사용
자료형 * 포인터 변수 이름 = NULL; (초기화)
int *p1 = NULL; // int형 자료의 주소를 저장하는 포인터 변수
char *p2 = NULL; // char형 자료의 주소를 저장하는 포인터 변수
double *p3 = NULL; // double형 자료의 주소를 저장하는 포인터 변수
포인터에도 자료형이 있고, 그 자료형에 알맞는 값만 가리킬 수 있다
사용은 주소 연산자와 참조 연산자를 통해 이루어진다.
주소 연산자 (&) 와 참조 연산자 (*)
& == 주소 연산자, 주소를 나타낸다.
* == 참조 연산자, 주소가 가리키는 값을 나타낸다.
int * p = NULL;
int a = 10;
p = &a; // 포인터 변수 p에 a변수를 가리키는 주소 저장
printf("%d", *p); // p라는 포인터 변수가 가리키는 주소가 가리키는 값 출력
따라서 *p는 p가 가리키는 주소의 값 == a 변수 주소가
가리키는 값 == a변수
출력값은 10이 될 것.
여기서 유의해야 할 것은 p의 값, *p의 값, &p의 값이 모두 다르다는 것이다.
p = a의 주소
&p = p의 주소
*p = a (p가 가르키고 있는 주소 -> 가 가르키고 있는 값 == a)
이걸 이해한다면 다차 포인터 또한 쉽게 이해할 수 있다.
직접 접근과 간접 접근
#include <stdio.h>
int main( )
{
char c = 'A';
char *cp = NULL;
cp = &c; // 주소 저장
printf("%x %c %c \n", &c, c, *&c);
printf("%x %x %x \n", &cp, cp, *&cp);
printf("%c \n", c); // 직접 접근
printf("%c \n", *cp); // 간접 접근
return 0;
}
변수를 직접 호출하면 직접 접근이고
포인터를 통해 호출하면 간접 접근이다.
다차원 포인터
다차원 포인터는 <<a의 주소를 가리키는 포인터>의 주소를 가리키는 포인터>의 주소를 가리키는 포인터
이런 식으로 진행하는 포인터이다.
그림으로는 이렇게 나타밸 수 있고 여기서 짚고 가야 할 것은
p1 == a의 주소 == &a
*p1 == a (10)
&p1 == p1의 주소 == p2
p2 == p1의 주소
*p2 == p1의 주소가 가리키는 값 (p1) == a의 주소
**p2 == <p1의 주소가 가리키는 값>이 가리키는 것의 값 == a
실습과 함께하면 더 정확하게 이해할 수 있다.
#include <stdio.h>
int main() {
int a = 115;
int *p1 = &a;
int **p2 = &p1;
printf("%x %x %x\n", &a, p1, *p2); // a의 주소는 p1과 같고 p2가 가리키는 값과 같다.
printf("%x %x\n", &p1, p2); // p1의 주소는 p2와 같다.
printf("%d %d %d", **p2, *p1, a); // p2가 가리키는 값의 가리키는 값은 p1이 가리키는 값.
return 0;
}
앞에서 글로 설명한 내용을 코드로 구현했다.
다음은 '포인터의 사이즈는 8' 이라는 것을 이용해 위와 비슷한 느낌으로 이해를 돕기 위한 코드이다.
#include <stdio.h>
int main() {
int num1 = 10;
int* ip1 = NULL;
int** ip2 = NULL;
int*** ip3 = NULL;
ip1 = &num1;
ip2 = &ip1;
ip3 = &ip2;
printf("%d %d %d %d \n", num1, *ip1, **ip2, ***ip3);
printf("%x %x %x %x \n", &num1, ip1, *ip2, **ip3);
printf("%x %x %x \n", &ip1, ip2, *ip3);
printf("%x %x \n", &ip2, ip3);
printf("%d %d \n", sizeof(int), sizeof(int*));
printf("%d %d\n", sizeof(int**), sizeof(int***));
printf("%d %d \n", sizeof(num1), sizeof(ip1));
printf("%d %d\n", sizeof(ip2), sizeof(ip3));
return 0;
}
포인터의 가감산
64비트에서 포인터의 크기는 8비트이다!
#include <stdio.h>
int main() {
int num = 10;
int* ip = NULL;
int** ipp = NULL;
ip = #
ipp = &ip;
printf("%d %d %d\n", sizeof(num), sizeof(ip), sizeof(ipp));
// 4 8 8
printf("%x %x %x \n", &num, &ip, &ipp);
//6b243028 6b243020 6b243018
printf("%x %x %x \n", &num + 1, &ip + 1, &ipp + 1);
//6b24302c 6b243028 6b243020
printf("%d %x %x \n", num, ip, ipp);
//10 6b243028 6b243020
printf("%d %x %x \n", num + 1, ip + 1, ipp + 1);
//11 6b24302c 6b243028
return 0;
}
참조 연산자, 주소 연산자 또는 연산자 없이 표현하고 있는 값이
포인터인지, 그냥 int형인지 알아야만 포인터의 가감산을 이해할 수 있다.
위 코드의 경우 &num은 int형 변수의 주소이므로 &num + 1을 했을 때 int형 변수의 크기인 4가 더해진다.
따라서 같은 이유로 ip + 1, *ipp + 1 또한 4가 더해질 것이다.
또한 &ip는 포인터 변수의 주소이다. 따라서 &ip + 1을 했을 때 8이 더해진다.
같은 이유로 ipp + 1 또한 8이 더해진다.
&ipp의 경우도 마찬가지로, 포인터 변수의 주소이기 때문에 8이 더해진다.
정리하면,
4가 더해지는 경우 == int형 변수의 주소를 가리키는 경우 == &num + 1, ip + 1, *ipp + 1
8이 더해지는 경우 == 포인터 변수의 주소를 가리키는 경우 == &ip + 1, ipp + 1, &ipp + 1
'C' 카테고리의 다른 글
[C] 배열 포인터, 포인터 배열 (0) | 2023.10.26 |
---|---|
[C] 포인터와 함수 (0) | 2023.10.26 |
[C] C의 문자열, 문자 배열 (0) | 2023.10.18 |
[C] 조건 연산자 (삼항 연산자) 백준 2562 (0) | 2023.09.11 |
[C] 백준 2577 (0) | 2023.06.26 |