#include <iostream>
int main()
{
/*
포인터 연산 : 덧셈, 뻴셈을 지원한다.
일반적인 사칙연산은 1을 더할 경우 단순하게 값이 1이 증가하게 되지만 포인터의 경우 1을 더하면 단순하게 1이 증가하는 개념이 아닌 해당 포인터 타임(int면 int, float 포인터 이면 float)의 메모리 크기만큼이 증가하게 된다.
즉, 2를 더하게 된다면 int포인터일 경우 int의 크기인 4바이트 * 2만큼 메모리주소의 값이 증가하게 되는 것이다.
배열의 index 접근 = 포인터 연산
*/
int intArray[10] = {};
for (int i = 0; i < 10; ++i)
{
intArray[i] = i + 1;
//Array = 배열의 시작 주소
}
int* intpArray = intArray;
std::cout << "intpArray : " << intpArray << std::endl;
std::cout << "intpArray + 1 : " << intpArray + 1 << std::endl;
std::cout << "intpArray + 2 : " << intpArray + 2 << std::endl;
//정확하게 int의 데이터 크기인 4byte 만큼 증가했다.
intpArray[1] = 300;
*(intpArray + 1) = 600;
++intpArray;//주소가 이동되었으므로 현재 intpArray[0] = 아까의 intpArray[1]이 된다.
intpArray[0] = 900;
for (int i = 0; i < 10; ++i)
{
std::cout << "intpArray[" << i << "] = " << intpArray[i] << std::endl;
}
bool boolArray[10] = {};
bool* boolpArray = boolArray;
std::cout << "\nboolpArray : " << boolpArray << std::endl;
std::cout << "boolpArray + 1 : " << boolpArray + 1 << std::endl;
std::cout << "boolpArray[1] : " << &boolpArray[1] << std::endl;
std::cout << "boolpArray + 2 : " << boolpArray + 2 << std::endl;
//1byte만큼만 증가했음.
const int Number = 999;
int Number1 = 1010;
int Number2 = 3030;
//int* pNumber = &Number; //에러가 발생
//Number는 상수이므로 주소를 붙여주면 const * 가 된다.
//pNumber 은 일반 포인터 타입이므로 타입이 달라서 에러가 발생한다.
//포인터 타입 왼쪽에 const를 붙여줄 경우 해당 포인터 변수는 다른 변수의 메모리 주소로 값을 변경하는 것이 가능하다.
//단, 해당 주소에 접근하여(역참조) 값을 변경하는 것은 불가능하다. 값을 가져다 사용하는 용도로는 가능하다. 한마디로 가져다 보는 건 가능하지만 쓰기는 불가능.
const int* pNumber = &Number; //정상작동
pNumber = &Number1; //정상작동
pNumber = &Number1;
//*pNumber = 500; // 에러발생: 식이 수정할 수 있는 lvalue여야 합니다.( = 변수여야 합니다)
//포인터 타입의 오른쪽에 const를 붙일 경우 const int타입의 메모리 주소는 지정이 불가능하다.
//포인터 변수 자체를 상수로 만들어 버린 것.
//처음에 지정된 주소 이외에 다른 변소의 주소로 변경이 불가능하다.
//단, 역참조를 통해 참조하는 대상의 값을 변경하는것이 가능하다.
//int* const pNumber2 = &Number; //에러발생
int* const pNumber2 = &Number1;
pNumber = &Number2;
*pNumber2 = 9090;
//둘 다 상수로 만들어버릴 수도 있다.
//위의 2가지 특징이 모두 적용되어 참조하는 대상도 변경이 불가능하고 참조하려는 대상의 값도 변경이 불가능하다.
const int* const pNumber3 = &Number1;
//pNumber3 = &Number2;
//*pNumber3 = 2222;
//기본 문법이므로 반드시 외울 것
//나중에 가면 팀 작업을 많이 하게 될것이므로
//내가 만든 데이터는 내가 관리를 해야 한다.
//void : 타입이 없다는 의미이다.
//일반 void 타입은 변수 선언시에는 사용할 수 없다.
//void는 포인터 타입을 선언하여 사용이 가능하다.
//void 포인터를 선언하면 타입이 없는 포인터가 선언되어 어떠한 변수타입의 메모리 주소라도 다 저장이 가능하다.
//단, 반쪽자리 만능이다. 왜냐하면 역참조가 불가능하다.
//그래서 저장된 주소로 역참조를 하기 위해서는 형변환을 통해 접근해야 한다.
void* pNumber4 = &Number2;
//*pNumber4 = 191919; //에러발생
*((int*)pNumber) = 191919;
std::cout << "*((int*)pNumber) = " << *((int*)pNumber) << std::endl;
*((float*)pNumber) = 191919;
std::cout << "*((float*)pNumber)로 잘못 형변환을 했을 경우 결과가 이상해진다 => " << Number2 << std::endl;
/*
이중포인터 : 포인터의 포인터
일반 포인터가 일반 변수의 메모리 주소를 저장하기 위한 변수라면 이중포인터는 포인터 변수에 메모리 주소를 저장하기 위한 변수이다.
삼중포인터 : 이중포인터 변수의 메모리 주소를 저장하기 위한 변수이다.
*/
int* pNumber5 = &Number1;
int** ppNumber = &pNumber5;
//ppNumber는 pNumber5의 주소를 가지고 있으므로 역참조를 하면 pNumber5가 가지고 있는 메모리 주소를 변경할 수 있다.
//여기서는 pNumber5가 Number1의 메모리 주소를 가지고 있었는데 Number2의 주소를 가질 수 있도록 변경한 것이다.
*ppNumber = &Number2;
**ppNumber = 334455;
std::cout <<"Number1 : " << Number1 << std::endl;
std::cout <<"Number2 : " << Number2 << std::endl;
//포인터 문법 끝
return 0;
}