포인터
포인터를 이해하기 위해선 [[Memory Features(General)]]를 이해하면 편하다.
포인터의 개요
포인터 변수는 변수 타입 중 하나!
- 포인터 변수 값: 메모리의 주소
- 메모리에 뭘 저장? 변수 vs 포인터 : 값 저장 vs 주소 저장
- 포인터 타입: 주소에 저장된 데이터 타입.
포인터 사용 이유!
- 중요!)동적 할당을 통해 힙 영역의 메모리([[Heap Memory(General)]])를 사용
- 중요!)변수의 범위 문제로 접근할 수 없는 곳의 데이터를 사용(참조자와 유사함)
- 배열의 효율적인 사용
- 다형성은 포인터를 기반으로 구현됨
- 시스템 응용 프로그램 / 임베디드 프로그래밍은 메모리 직접 접근이 필요함
포인터의 정의
기존 변수 타입 뒤에 *
을 붙여 포인터 변수를 정의한다.
원문
1
2
3
4
5
// 정석적인 정의와 초기화
variable_type* pointer_name = nullptr;
//야매. 쓰레기 값이 들어가니 지양할 것
variable_type* pointer_name;
참고: nullptr(Cpp)
예시
1
2
int* int_ptr;
double* double_ptr = nullptr;
주소로의 접근
변수의 주소값 얻어오기
포인터 변수는 주소값을 저장하므로, 피연산자 변수의 주소값을 얻어와야한다!
이를 위해 주소연산자(&
) 를 사용한다.
- 연산자가 적용되는 피연산자의 주소값 반환
- 주의)피연산자는 주소를 얻을 수 있는 종류여야함(I-value)
예시
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>
int main()
{
int a = 10;
int* b = &a;
std::cout << "a 변수:" << a << std::endl;
std::cout << "a 주소a:" << &a << std::endl;
std::cout << "a 주소b:" << b << std::endl;
//컴파일 에러: 10은 주소를 얻을 수 없다!
//std::cout << "10 주소:" << &10 << std::endl;
return 0;
}
1
2
3
a 변수:10
a 주소a:012FFE88
a 주소b:012FFE88
포인터 주소의 이해
포인터 변수의 크기와 포인터가 가리키는 대상의 크기는 별개의 존재이다!!
포인터 주소의 크기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>
int main()
{
int* b;
//컴파일 에러: 과거 VS에선 쓰레기값이 있는 Ptr도 출력했지만, 17년 이후 쓰레기값 Ptr는 컴파일 에러가 나온다.
//std::cout << "b 변수:" << b << std::endl;
int a = 10;
b = &a; //쓰레기값 출력이 안돼 임의 변수 생성
std::cout << "b 변수:" << b << std::endl;
std::cout << "b 주소:" << &b << std::endl;
std::cout << "b 크기:" << sizeof(b) << std::endl;
b = nullptr;
std::cout << "b 변수:" << b << std::endl;
return 0;
}
1
2
3
4
b 변수:012FFAD0
b 주소:012FFAC4
b 크기:4
b 변수:00000000
포인터도 변수라서 포인터의 주소가 있고 값을 변경할 수 있다. 그리고 sizeof(Cpp) 함수를 사용해 메모리 공간을 사용을 확인해 봤다. 여기서 포인터는 int
타입이어서 4byte를 사용한게 아니라, 포인터 변수는 모두 같은 크기(4byte) 이다. 타입이 어떻든 간에 모두 ‘주소값’ 이라는 데이터를 저장하기 때문이다.
포인터의 타입은 왜 필요한가.
포인터의 크기는 타입과 무관하게 언제나 4byte란걸 알았다. 그럼 타입은 왜 필요한걸까.
그 이유는 포인터가 해당 주소의 값에 접근할 때 몇 바이트 크기인지 알아야 하기 때문이다. 포인터의 타입은 해당 변수의 크기를 나타내기도 하지만, 동시에 주소 해석 방법을 제시하기도 한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>
int main()
{
int a = 10;
double b = 10.1;
int* ptr = nullptr;
ptr = &a;
//컴파일 에러: b는 double 타입인데 포인터는 int 타입이다. 주소 해석 불가!
//ptr = &b;
return 0;
}
역참조(접근)
포인터의 주소에 저장된 데이터에 접근
* 연산자를 사용해 접근이 가능하다
- 포인터를 만들 때 사용한
*
과는 개념이 다르다.- 생성
*
: 메모리에 포인터의 공간 생성 - 사용
*
: 포인터의 주소 확인, 해당 주소로 이동
- 생성
예시
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <iostream>
int main()
{
int a = 10;
int* ptr = &a;
std::cout << "전)변수값:" << a << std::endl;
std::cout << "전)역참조:" << *ptr << std::endl;
*ptr = 20;
std::cout << "후)변수값:" << a << std::endl;
std::cout << "후)역참조:" << *ptr << std::endl;
return 0;
}
1
2
3
4
전)변수값:10
전)역참조:10
후)변수값:20
후)역참조:20
이하 내용은 계속 추가될 듯..