1. What is a Pointer?
포인터 타입 변수는 다른 변수의 메모리 주소를 저장한다
포인터는 주소 전달 방식으로서 함수의 매개변수에 사용되고 배열에 아주 효과적으로 사용되며 힙 영역에 메모리를 동적으로 할당할 때 사용한다 또한 임베디드 시스템에서 특정 메모리 주소 액세스 권한이 필요할 때 사용된다
타입* 변수명;으로 포인터 변수를 선언할 수 있다
이때 *는 수학적의미가 아닌 포인터 변수를 의미한다
포인터 변수도 일반 변수와 마찬가지로 초기화 하지 않으면 쓰레기값(Garbage Data)이 들어가게 된다 꼭 사용하기 전에 초기화를 하고 사용해야 한다
포인터 변수의 쓰레기값은 결국 주소이다 만약 이 주소가 프로그래머가 알지 못하는 유효한 곳이라면 접근 시 상당히 위험한 결과를 나타낼 수 있다
포인터 변수는 일반 변수와 같은 방식으로 초기화가 가능하다
nullptr은 말 그대로 가리키는 주소가 없다는 의미이다 (Null), nullptr인 포인터 변수를 참조하게 되면 크래쉬가 발생한다 (거의 대부분의 크래쉬 원인)
nullptr은 0으로 출력된다
int main()
{
int* Int_Ptr; //int타입 포인터 변수
double* Double_Ptr; //double타입 포인터 변수
//일반 변수와 같은 방식으로 초기화 가능
int* IntPtr1{};
int* IntPtr2{ nullptr };
int* IntPtr3 = nullptr;
return 0;
}
2. Access And Store Address in a Pointer Variable
C++에는 주소연산자 &가 존재한다 이는 변수의 메모리주소를 나타낸다 (lvalue여야 한다, 상수에 사용은 불가능하다)
따라서 포인터 변수에는 &변수;로 특정 변수의 메모리 주소값을 넣어줄 수 있다
포인터 변수의 크기는 전부 동일하다 (변수 자체의 크기가 아닌 주소를 담고 있기 때문에), 32bit설정이면 4byte, 64bit설정이면 8byte이다
포인터 변수에 주소를 넣어줄 때 포인터 변수의 타입과 주소를 타고 갔을때 나오는 변수의 타입을 항상 일치시켜야 한다 (다르면 컴파일 에러 발생)
int main()
{
int num{ 10 };
# //num변수의 메모리 주소를 나타낸다 ex) 0x61ff18
int* temp{ nullptr };
temp = #
cout << sizeof temp << endl; //설정에 따라 4, 8byte를 출력한다 (int*, double*, string* 등 타입에 상관없이 전부 크기가 동일함)
int IntScore{ 10 };
double DoubleScore{ 10.7 };
int* Score_Ptr;
Score_Ptr = &IntScore; //가능
Score_Ptr = &DoubleScore; //컴파일 에러 발생 -> 포인터 변수 타입과 주소를 타고 갔을 때의 변수 타입이 다르기 때문 (int*, double)
return 0;
}
3. Dereferencing a Pointer
포인터 변수가 가리키는 데이터에 접근하기 위해서는 포인터가 가리키는 주소를 타고 가야 한다 (Dereferencing Pointer)
*포인터변수;로 포인터가 가리키는 데이터에 접근이 가능하다 (이떄 *는 간접연산자라고 한다)
이렇게 접근한 데이터는 수정도 가능하다
int main()
{
int score{ 100 };
int* ScorePtr = &score; //score변수의 주소로 ScorePtr이라는 포인터 변수 초기화
*ScorePtr = 300; //ScorePtr 포인터 변수가 가리키고 있는 데이터에 접근해서 값 수정
cout << score << endl; //score값이 300으로 변경됨
//같은 방식의 string예제
string Name{ "Kelvin" };
string* NamePtr = &Name;
*NamePtr = "Jacob";
cout << Name << endl; //Jacob출력
return 0;
}
4. Dynamic Memory Allocation
동적 메모리 할당은 대표적인 포인터 사용 예시이다
우리는 프로젝트를 개발할 때 어느정도의 메모리가 필요한지 정확히 알 수 없다 따라서 런타임에 메모리를 할당할 수 있어야 한다 (Array, Vector의 차이와 같음)
Vector가 Array와 다르게 동적으로 크기를 조절할 수 있는 이유는 Vector는 메모리를 heap에서 할당/해제하기 때문이다
new라는 키워드로 heap에 동적메모리 할당이 가능하다 new 타입;으로 동적할당 한다 (타입의 크기에 맞는 동적 메모리를 할당하고 주소로 반환한다 따라서 포인터 변수에 할당이 가능하다)
배열타입은 다음과 같이 할당한다 new 타입[개수];
일반 배열과 마찬가지로 배열의 시작 주소가 반환된다
일반 변수, 포인터 변수와 마찬가지로 초기화 하지 않으면 쓰레기값이 들어가게 된다
메모리를 할당하고 다 사용했으면 항상 해제를 해주어야 한다 (동적 메모리 할당만 하고 해제하지 않으면 메모리 누수가 발생하여 메모리가 부족해진다 )
delete라는 키워드로 heap에 할당된 동적메모리를 해제할 수 있다 delete 포인터변수;로 해제한다 이때 포인터 변수는 동적 할당 한 메모리 주소를 갖고있는 변수이다
배열타입은 다음과 같이 해제한다 delete[] 포인터변수;
delete를 하는 순가 해당 포인터 변수는 nullptr이 되기 때문에 이미 delete된 메모리 주소를 참조하려 하면 크래쉬가 발생한다
동적할당은 stack에서는 불가능하고 오직 heap영역에서만 가능하다
int main()
{
int* Int_Ptr{ nullptr };
Int_Ptr = new int; //heap에 int크기에 맞는 동적 메모리 할당 -> 주소로 반환하기 때문에 포인터 변수에 할당
*Int_Ptr; //Garbage Data
*Int_Ptr = 10; //포인터 변수가 가리키는 값을 10으로 변경
delete Int_Ptr; //Int_Ptr이 가리키고 있는 메모리 해제
int* ArrayPtr{ nullptr };
int Size{};
cin >> Size;
ArrayPtr = new int[Size]; //배열 타입 동적 메모리 할당
delete[] ArrayPtr;
return 0;
}
5. The Relationship Between Arrays and Pointers
배열의 이름은 배열의 시작 주소이고 포인터 변수는 주소를 담는 변수이다 따라서 포인터 변수에 배열 이름을 넣을 수 있다 -> 포인터 변수와 배열 이름은 동일하게 사용될 수 있다
그 결과 배열의 이름을 넣은 포인터 변수로도 배열의 인덱스 값에 접근이 가능하다 -> 배열의 이름을 넣은 포인터 변수명[index];로 접근이 가능하다는 의미이다
배열의 이름을 넣은 포인터 변수에 간접연산자를 사용하여 배열의 0번째 인덱스 값에 접근도 가능하다 -> *배열의 이름을 넣은 포인터 변수명; 으로 0번째 index값에 접근이 가능하다는 의미이다
포인터 변수에 값을 더하게 되면 그 값만큼 증가하는게 아니라 해당 포인터 변수의 타입의 크기 * 값만큼 증가하게 된다
ex) int* 일경우에 3을 더하면 0x61ff10 -> 0x61ff22가 된다 (int는 4byte이기 때문에 12가 늘어남)
포인터 변수에 값을 더한다는건 그 만큼 주소를 이동한다는 의미이다 따라서 배열의 원하는 index값에 접근할 때 사용할 수 있다 (배열도 마찬가지로 사용 가능)
ex) *ScoresPtr는 배열의 0번째 값, *(ScoresPtr + 1);은 배열의 1번째 값이 된다
int main()
{
int scores[]{ 10,20, 30,40, 50 };
cout << scores << endl; //scores배열의 시작 주소
cout << *scores << endl; //scores배열의 첫번째 값 10
int* ScoresPtr{ scores }; //배열의 이름은 배열의 시작 주소이고 포인터 변수는 주소를 담을 수 있기 때문에 할당 가능
*ScoresPtr; //*scores와 마찬가지로 scores배열의 첫번째 값 10이 나온다
ScoresPtr[1]; //배열 이름으로 인덱스에 접근하는 방식을 포인터 변수로도 할 수 있다
cout << scores << endl; //scores배열의 시작 주소
cout << *(ScoresPtr + 1) << endl; //+1이 되었기 때문에 다음주소로 이동 -> scores배열의 1번째 index값인 20이 나오게 된다
cout << *(scores + 1) << endl; //+1이 되었기 때문에 다음주소로 이동 -> scores배열의 1번째 index값인 20이 나오게 된다
int* TestPtr;
int TestVal{ 100 };
TestPtr = &TestVal;
cout << TestPtr << endl; //0x61ff10
cout << TestPtr + 2 << endl; //0x61ff18; int는 4byte이기 때문에 8이 늘어남
return 0;
}
'C++' 카테고리의 다른 글
14. OOP Class & Object (1) (77) | 2024.02.22 |
---|---|
13. Pointer And Reference (2) (63) | 2024.02.14 |
11. Function (2) (108) | 2024.01.26 |
10. Function (1) (127) | 2024.01.25 |
9. Character & String (134) | 2024.01.08 |