포인터(Pointer)
프로그램은 메모리를 사용하며 메모리는 변수를 저장한 주소를 가지고 있음
프로그램의 메모리 주소값을 통한 접근방법으로 다양한 작업이 가능
포인터 변수는 메모리 주소를 저장하는 변수이다.
자료형 뒤에 *를 붙여 선언할 수 있다.
int num = 100;
int *ptr = #
cout << "num이 저장된 메모리 주소 " << &num << endl;
cout << "ptr이 가지는 데이터 주소값 " << ptr << endl;
이러면 ptr 은 num 의 주소값을 갖게 되고 주소 안에는 num과 똑같이 100이 들어있다.

이 이후에 num 값에 변동이 있다면 ptr은 당연히 같은 주소를 가리키고 있기 때문에 *ptr의 값도 바뀌게 된다.
주소값을 알면 그에 해당하는 값이 무엇인지 알 수 있고 접근도 가능하다.
위 예로 들면 int *ptr = #에서 주소 ptr에 위치한 값을 보려면 *ptr을 하게 되면 알 수 있다.
주의사항
주의사항으로는 포인터 변수를 초기화 없이 사용하게 되면 쓰레기 값을 주소로 판단하여 프로그램을 손상시킬 수 있다.
따라서 포인터 변수가 주소값을 가지고 있지 않는 경우에는 nullptr로 초기화를 해야한다.
포인터의 크기
포인터의 크기 = 주소값의 크기 = 메모리가 주소값을 표현하는 단위
32bit 프로그램은 32bit = 4 byte, 64bit 프로그램은 64bit = 8byte의 크기를 갖게 된다.
void Swap1(int x, int y)
{
int temp = x;
x = y;
y = temp;
}
void Swap2(int *x, int *y)
{
int temp = *x;
*x = *y;
*y = temp;
}
int main()
{
int a = 10;
int b = 20;
Swap1(a, b);
Swap2(&a, &b);
}
위 상태에서 Swap1()을 사용하면 값은 바뀌지 않는다.
main()함수에서의 a, b는 Swap1()에서의 x, y와 엄밀히 따지면 다르다.
Swap1()을 호출하면 매개변수 x와 y가 생성되고 그 값이 Stack에 할당된다. 그리고 a와 b를 각각 x와 y에 값을 복사한다.
Swap의 값 자체를 복사해서 새로운 메모리 공간에 할당한 것이다.
이것을 Call by Value 라고 부른다.
반면 Swap2()는 주소값을 보내주는데, 이렇게 되면 맨 위의 상황처럼 같은 주소값을 갖고 있기 때문에 값에 변화를 주면 함수가 끝이 나도 실제 값에 변화가 생기게 된다.
이것을 Call by Reference 라고 부른다.
얕은 복사와 깊은 복사
얕은 복사
얕은 복사는 객체를 복사할 때 참조 타입의 멤버가 있다면, 참조값이 복사 된다.
즉, 얕은 복사는 동적 할당을 받은 변수의 주소값을 공유하게 된다.
class MyIntArray {
public:
int size;
int *data;
MyIntArray(int size) {
this->size = size;
data = new int[size];
}
MyIntArray(const MyIntArray &other);
};
MyIntArray::MyIntArray(const MyIntArray &other)
{
this->size = other.size;
this->data = other.data;
}
int main()
{
MyIntArray a(5);
for (int i = 0; i < a.size;i++) {
a.data[i] = i;
}
MyIntArray b = a;
a.data[0] = 10;
}
아래 그림처럼 a.data와 b.data는 같은 주소값을 공유하고 있다.
이렇게 같은 주소값을 공유하는 상황에서 a.data를 delete 한다면 이상한 값이 나오는걸 확인할 수 있다.

깊은 복사
깊은 복사는 얕은 복사와 다르게 참조 타입의 주소 공간도 새롭게 할당하여 복사하는 것을 뜻한다.
값은 복사를 하되 주소는 새롭게 할당 받으니 기존의 객체에 변화가 생겨도 복사된 개체에는 영향을 끼치지 않는다.
class MyIntArray {
public:
int size;
int *data;
MyIntArray(int size) {
this->size = size;
data = new int[size];
}
MyIntArray(const MyIntArray &other);
};
MyIntArray::MyIntArray(const MyIntArray &other)
{
this->size = other.size;
this->data = new int[size];
for (int i = 0; i < other.size; i++) {
this->data[i] = other.data[i];
}
}
int main()
{
MyIntArray a(5);
for (int i = 0; i < a.size;i++) {
a.data[i] = i;
}
MyIntArray b = a;
a.data[0] = 10;
}

깊은 복사를 구현하면 참조 타입의 멤버를 새로운 주소를 할당해서 아예 다른 주소값을 갖게 된다.
이렇게 하면 a의 데이터 혹은 b의 데이터를 변경하거나 해제하여도 서로 간의 간섭은 없어지게 된다.
[출처]https://kangworld.tistory.com/75
https://kangworld.tistory.com/64