메모리 공간은 일차원 배열
메모리 배열의 원소는 1byte씩 할당
int를 할당하면 1,2,3,4개의 공간을 할당하는 것임
(0은 다른 용도로 사용하기 위해서 빼 놓음)
이것을 주소라고 부름(메모리 주소-address)
변수의 주소에 접근하는 개념이 바로 포인트
포인터와 관련된 연산자(operator): 모두 단항 연산자
포인터 타입 *(asterisk)
int x{10};
int *p; //int* p;도 가능함
p = 19;
// 타입이 맞지 않는 다는 에러
// int의 포인터 타입(int*)과 int는 서로 다른 타입
// int 포인터 타입은 주소만 들어갈 수 있음 (정수는 들어갈 수 없음)
p = &x;
// x의 주소를 만드는 구문
// x라는 변수에는 값 10이 들어가고
// p라는 변수에는 x의 주소값(0x~)가 들어가는 것
std::cout << &x << std::endl;
std::cout << &p<< std::endl;
//바이트단위의 주소가 나옴 0x~~~~
//위 두줄의 결과값 000000D9E20FFBC4(x) 000000D9E20FFBE8(p) : 서로 다른 곳엠 만들어 짐
std::cout << p << std::endl;
//출력하면 x의 주소값 000000D9E20FFBC4이 출력됨
//포인터 p는 x라는 변수의 주소를 가리키는 것
역참조(de-reference)
역참조 연산자 *
int a{ 1 }, b{ 2 };
int* p;
p = &a;
std::cout << *p << std::endl; //역참조 : 결과는 a의 값이 1
*p = 10;
//a의 값이 p를 통해서 10에서 1로 바껴짐
//p가 가르키는 대상이 a이므로
std::cout << a << std::endl; //결과 10
왜 포인터가 어려운가??
참조와 역참조의 관계를 생각해보자
x * y; //x곱하기 y
int* p; //포인터 변수만 가능한 참조
*p;//포인터 변수 역참조(안으로 들어가서 값에 접근)
int x{ 2 }, y{ 2 };
int *p1{ &x }, *p2{ &y };
std::cout << *p1 * *p2 << std::endl; // 4
int x{ 1 };
char c{ 'a' };
int* p;
p = &x;
p = &c;
//p라는 변수는 int를 가리킨다는 의미 하지만 위 문장은 char
char* p2;
p2 = &c;
포인터의 크기는 고정이다
--
x64 : 8바이트
x86 : 4바이트
int * 변수;
포인터 변수의 선언에 사용된 타입은 역참조를 위해서 반드시 필요한 정보(값을 가져오기 위해서 타입의 정보를 알아야함)
반드시 가리킬 대상의 타입을 써줘야함
int x{ 1 };
char c{ 'a' };
int* p;
p = &x;
p = &c;
//p라는 변수는 int를 가리킨다는 의미 하지만 위 문장은 char
char* p2;
p2 = &c;
//둘 다 크기는 8바이트(포인터 변수는 크기가 고정됨 : 메모리의 주소가 들어오기 때문)
//레지스터의 크기와 동일(32비트라면 4바이트)
//왜 가리키는 대상의 타입을 써줘야하나요? 크기가 동일한데
//메모리는 단순히 바이트로 나열된 배열 그런데 주소를 저장하면 주소밖에없는데
//역참조를 해서 들어가려면 현재 가지고 있는 주소에서 몇 개까지 바이트를 엮어서 값으로 인식해야하는지 포인터가 알아야함
//그렇기 때문에 타입의 정보가 들어가게 되는 것
포인터의 연산
--
int x{ 1 };
std::cout << x + 1 << std::endl;
// 변수를 만들면 연산이 가능
int* p{ &x };
std::cout << p << std::endl;
std::cout << p + 1 << std::endl;
//포인터 변수도 변수라서 연산이 가능
//결과값 : 00000093438FFC64
//00000093438FFC68
//4바이트 차이 : int(4바이트)를 가리키는 포인터이기 때문
//+1의 의미 int가 한개 건너뛴다의 느낌
//그래서 포인터가 가리키는 타입이 필요
//곱하기 나누기는 안됨 : 정확히 나올 수 없기 때문
포인터의 기본값 (nullptr)
--
int main()
{
int x{}; // 기본값 0 {0};
int* p{}; // 주소의 0번을 가리킴 그래서 0 : {0};
}
void MyFunc(int x)
{
}
void MyFunc(int* x)
{
}
int main()
{
MyFunc(nullptr); //포인터의 기본값
}
//함수의 모호함
//그래서 cpp에서 포인터의 기본값으로
nullptr = Null Pointer
타입이 없는 포인터 (void *)
--
누구나 가리킬 수 있다
역참조 불가능함 (타입을 모르기 때문)
포인터의 연산도 불가능함(타입의 크기만큼 연산하기 때문)
프로그래머가 명시적으로 변환하기는 가능함
int main()
{
int x{ 1 };
void* p;
p = &x;
std::cout << *p;
//가리키는 것은 가능하지만 역참조가 불가능함
std::wcout << *(int*)p << std::endl;
//void포인터를 int포인터로 변환해서 역참조해서 들어감
}
int main()
{
int x{ 1 };
void* p;
p = &x;
char r = *(char*)p;
char g = *((char*)p + 1);
p = (char*)&x;
char r = *p;
}