들어가기에 앞서 우리가 앞에서 공부했던 ‘메모리 구조’를 다시 한번 보고 상기시켜 보자.
그러기 위해서는 포인터라는 개념을 완벽히 숙지해야 한다.
variable_type* pointer_name = nullptr;
초기화를 하지 않으면 쓰레기 값이 들어있는 상태이므로 방지가 필요
nullptr은 아무것도 가리키지 않는 상태를 의미
포인터 변수는 변수의 타입 중 하나
포인터의 타입
포인터를 쓰는 이유
ex)
int a = 10;
int* ptr = null;
ptr = &a;
cout << sizeof(ptr); // 4byte
이때 ptr의 크기가 왜 4바이트냐? 가리키는 변수가 int라서 그런가?
#include <iostream>
int main()
{
int score = 10;
int* ptr = &score;
std::cout << *ptr << std::endl;
*ptr = 20;
std::cout << *ptr << std::endl;
std::cout << score << std::endl;
return 0;
}
위와 같이 *ptr을 단독적으로 사용한다면 “ptr이라는 포인터 변수가 가리키고 있는 변수에 int형 변수에 접근하여 그 안에 저장된 값에 접근한다!” 라고 생각하면 좋다!
즉 , 포인터가 가리키고 있는 변수의 값을 조작하고 싶으면 역참조를 사용하면 된다.
런타임에 힙 메모리를 할당
힙 메모리는 스택과 달리 스스로 해제되지 않음!
사용이 끝나고 해제하지 않으면 메모리 누수 발생!
ex)
int n;
std::cout << "Enter the number of elements: ";
std::cin >> n;//사용자의 입력의 크기에 따라서 달라진다.
// 동적 메모리 할당
int* arr = new int[n];
new의 역할 ( C 의 malloc) Heap 메모리 공간에 int 하나를 담을 수 있는 메모리 주소를 찾고 주소 값을 반환
#include <iostream>
using namespace std;
int main()
{
int* ptr = nullptr;
ptr = new int; // heap에 int만큼의 메모리 할당
cout << ptr << endl; //주소 값
cout << *ptr << endl; // 쓰레기 값
*ptr = 100;
cout << *ptr << endl;
delete ptr;// 해제해주고 ptr은 nulltptr이 된다.
ptr = nullptr;//컴파일러 마다 null처리를 해주는 것이 있고 안해주는 것이 있어서 초기화 해주는게 좋다.
return 0;
}
int* ptr = nullptr; //이는 스택에 존재
ptr = new int; // ptr이 가리키는 변수를 heap에 할당하고 해당 주소값을 저장한다.
#include <iostream>
using namespace std;
int main()
{
int* arrayPtr = nullptr;
int size = 0;
cout << "size of array?";
cin >> size;
arrayPtr = new int[size];
arrayPtr[0] = 10;
arrayPtr[1] = 20;
arrayPtr[2] = 30;
delete[] arrayPtr;
return 0;
}
차이점 : 배열은 주소 값을 정의 이후 변경 불가 , sizeof() 반환 값이 다름
직접 봐보자!
#include <iostream>
using namespace std;
int main()
{
int scores[] = { 100,95,90 };
cout << sizeof(scores) << endl;
int* scoresPtr = scores;
cout << sizeof(scoresPtr) << endl;
scores = #/* *********중요******* 불가능하다.
배열의 이름음 항상 처음 선언했던
배열의 첫 번째 요소만 가리킨다. */
return 0;
}
#include <iostream>
using namespace std;
int main()
{
//정적 할당
int scores[] = { 100,95,90 };
cout << scores << endl; //0x...
cout << *scores << endl; // 100
int* scoresPtr = scores;
cout << scores << endl;//0x...
cout << *scores << endl; //100
return 0;
}
우리는 배열의 이름이 항상 배열의 첫 번째 요소를 가리키는 포인터인 것을 알게 되었다.
자 그럼 이제 아래의 코드를 한번 봐보자.
#include <iostream>
using namespace std;
int main()
{
//정적 할당
int scores[] = { 100, 95 , 90 };
int* scoresPtr = scores;
cout << scoresPtr << endl; //0x1234
cout << (scoresPtr + 1) << endl; //0x123404
cout << (scoresPtr + 2) << endl; //0x123408
cout << *scoresPtr << endl; // 100
cout << *(scoresPtr + 1) << endl; // 95
cout << *(scoresPtr + 2) << endl; // 90
return 0;
}
int arr[3];
int* ptr = arr;
cout << arr + 1 << endl;
cout << arr + 2 << endl;
arr + 1 의 의미는 arr이 가리키는 변수(요소)의 주소 값 + 1 * (data size of int)
우리는 여기서 포인터 변수를 선언 할 때 data type을 넣는 이유를 알게 되었다.
arr [1] 과 *(arr + 1)의 차이점?