포인터
포인터는 다른 변수나 메모리의 주소를 저장하는 변수이다.
코드를 작성하면서 포인터의 개념이 부족하다고 느껴 포인터에 대해 공부하고 기록하려고 한다.
I. 포인터 선언 및 초기화
포인터는 특정 데이터 형식의 변수를 가리키는 변수이다.
포인터를 선언할 때는 데이터 형식 뒤에 '*' 를 붙인다.
int* ptr; // int 형 변수를 가리키는 포인터
char* cptr; // char 형 변수를 가리키는 포인터
또한 포인터는 다른 변수의 주소를 저장한다.
주소를 할당하려면 주소 연산자 '&' 를 사용한다.
int a = 10;
int* ptr = &a; // 'a' 변수의 주소를 ptr에 저장
II. 포인터의 역참조
포인터를 통해 실제 변수에 접근할 수 있다.
이를 역참조라고 하며 '*' 연산자를 사용한다.
int a = 10;
int* ptr = &a;
int b = *ptr; // ptr이 가리키는 변수의 값을 b에 저장 (b는 10이 됩니다)
*ptr = 20; // ptr이 가리키는 변수의 값을 20으로 변경 (a는 이제 20이 됩니다)
III. NULL 포인터
null포인터는 어떤 유효한 메모리 주소도 가리키지 않는 포인터이다.
C++에서는 nullptr 키워드를 사용하여 null 포인터를 초기화할 수 있다.
int* ptr = nullptr;
이 때 널 포인터는 다음과 같이 비교할 수 있다.
if (ptr == nullptr) {
// ptr이 널 포인터인지 확인
}
if (ptr2 == 0) {
// ptr2가 널 포인터인지 확인
}
if (ptr3 == NULL) {
// ptr3가 널 포인터인지 확인
}
IV. 포인터 산술 연산
포인터는 배열과 함께 사용될 때 주로 포인터 산술 연산을 사용한다.
포인터에 정수를 더하거나 빼는 것은 포인터가 가리키는 데이터 타입의 크기 단위로 이동한다.
int arr[5] = {1, 2, 3, 4, 5};
int* ptr = arr;
ptr++; // ptr은 이제 arr[1]을 가리킵니다 (2를 가리킴)
ptr--; // ptr은 다시 arr[0]을 가리킵니다 (1을 가리킴)
V. 배열과 포인터
배열 이름은 유사하게 동작한다.
배열 이름 자체가 배열의 첫 번째 요소의 주소를 나타낸다.
int arr[5] = {1, 2, 3, 4, 5};
int* ptr = arr; // ptr은 arr의 첫 번째 요소의 주소를 가리킴
for(int i = 0; i < 5; ++i) {
std::cout << *(ptr + i) << std::endl; // 배열 요소를 출력
}
VI. 포인터와 함수
포인터는 함수 인자로 사용될 수 있으며, 이를 통해 함수는 원본 데이터를 수정할 수 있다.
void increment(int* ptr) {
(*ptr)++;
}
int main() {
int a = 5;
increment(&a); // a는 이제 6이 됩니다
return 0;
}
VII. 동적 메모리 할당
동적 메모리 할당은 런타임에 메모리를 할당할 수 있게 한다.
'new'와 'delete' 연산자를 사용하여 동적 메모리를 관리할 수 있다.
int* ptr = new int; // int 형 변수를 위한 메모리 할당
*ptr = 10;
delete ptr; // 할당된 메모리 해제
int* arr = new int[5]; // int 형 배열을 위한 메모리 할당
delete[] arr; // 할당된 배열 메모리 해제
VIII. 이중 포인터
포인터를 가리키는 포인터를 선언할 수 있다.
이를 이중 포인터라 한다.
int a = 10;
int* ptr = &a;
int** ptr2 = &ptr;
std::cout << **ptr2 << std::endl; // 출력: 10
IX. const와 포인터
포인터와 함께 'const' 키워드를 사용하여 포인터나 포인터가 가리키는 값을 상수로 만들 수 있다.
const int* ptr1 = &a; // 포인터가 가리키는 값을 변경할 수 없음
int* const ptr2 = &a; // 포인터 자체를 변경할 수 없음
const int* const ptr3 = &a; // 포인터와 포인터가 가리키는 값을 모두 변경할 수 없음
X. 함수 포인터
함수의 주소를 저장하는 포인터도 선언이 가능하다.
int add(int a, int b) {
return a + b;
}
int main() {
int (*funcPtr)(int, int) = add;
std::cout << funcPtr(2, 3) << std::endl; // 출력: 5
return 0;
}
공부하며 작성한 코드
#include <iostream>
using namespace std;
int increment(int* ptr)
{
(*ptr)++;
return *ptr;
}
int main()
{
int a = 10; //변수 a = 10
int* address = &a; //a의 주소값
int b = *address; //address(a의 주소)가 가리키는 변수의 값
*address = 20; //주소가 가리키는 값을 20으로 변경, a가 20으로 변경된다
cout << "a의 주소 : " << address << "\n";
cout << "b의 값 : " << b << "\n";
cout << "주소가 가리키는 값 변경: " << a << "\n";
address = nullptr;
//nullptr = 주소를 가리키지 않는 포인터,
cout << "널 포인터 출력값: " << address << "\n";
//주소를 가리키지 않는 것 뿐이지, 값은 사리지지 않는다.
cout << "a값: " << a << "\n";
int* dangling = new int(10);
cout << "할당된 주소: " << dangling << "\n";
cout << "dangling이 가리키는 값: " << *dangling << "\n";
//delete: 동적 메모리 할당, 메모리 관리를 위해 사용하지 않는 포인터는 delete 해야한다.
delete dangling;
//주소를 여전히 출력하지만, 더이상 유효하지 않는다.
cout << "댕글링 포인터: " << dangling << "\n";
//댕글링 포인터 사용(위험한 동작)
//*dangling = 20;
//주석 해제 시 정의되지 않는 동작 발생 가능
//이는 다음과 같이 해결할 수 있다.
//delete로 메모리 해제 후 포인터를 nullptr로 설정한다.
dangling = nullptr;
if (dangling != nullptr)
{
*dangling = 20;
}
else
{
cout << "현재 nullptr를 가리킴" << "\n";
}
int arr[6] = { 1,2,3,4,5,6 };
int* ptr = arr; //배열의 첫번째 값을 가리킴, arr+1은 arr[1] 즉, 두번째 값
cout << "arr의 값 : " << *arr << "\n"; //배열에 포인터를 선언하면 arr[0]이 출력됨
for (int i = 0; i < 5; i++)
{
cout << *(ptr+i) << "\n";
}
//포인터는 함수 인자로 사용될 수 있음. 이를 통해 함수는 원본 데이터를 수정할 수 있다.
int result = increment(arr);
cout << "\n함수 값: " << result << "\n";
int Double_Pointer = 10;
int* DP_Address = &Double_Pointer;
int** DP_Address2 = &DP_Address;
cout << "이중 포인터: " << **DP_Address2 << "\n";
return 0;
}
'Etc.' 카테고리의 다른 글
게임 프로그래밍에서의 자료 구조 (0) | 2024.07.16 |
---|---|
내가 찾아보고 공부해봐야 하는 키워드 정리 (0) | 2024.07.16 |
댕글링 포인터(Dangling Pointer) (0) | 2024.05.16 |
[C++] Class (0) | 2024.05.13 |
BaekJoon 1931 문제에서.. (0) | 2024.04.09 |