배움 저장소
[코테용C++ 핵심 정리] 챕터4. 메모리 본문
포인터
ampersand &
int a = 1;
cout << &a << endl;
int *b = &a;
deference operator *
자료형이 정해져있으므로 b 주소에서 값을 가져올 수 있다.
cout << *b << endl;
*b = 123;
포인터의 초기값
nullptr 혹은 0을 사용한다.
double *c = 0; // nullptr;
포인터의 크기
포인터의 크기는 동일하다.
집 크기가 크다고 해서 주소가 더 길어지는 건 아니다.
cout << sizeof(int*) << endl; // 8byte
cout << sizeof(double*) << endl; // 8byte
포인터 디버깅
16진수인 포인터를 확인하기위해 10진수로 변환하자. size_t로 형변환해주면 된다.
int* b = 0;
cout << size_t(b) << endl;
포인터 연산과 배열
포인터 + 1의 연산은 포인터 주소 + 자료형 크기와 동일하다
int* b = 0;
cout << size_t(b) << endl; // 0
cout << size_t(b+1) << endl; // 4
cout << size_t(b+2) << endl; // 8
double* c = 0;
cout << size_t(c) << endl; // 0
cout << size_t(c+1) << endl; // 8
cout << size_t(c+2) << endl; // 16
- 포인터를 배열처럼 사용할 수 있다.
- 배열의 이름은 포인터이다. 배열의 이름은 배열의 첫 번째 원소의 주소를 가리킨다.
- 배열의 인덱싱은 배열의 첫 번째 원소로부터 몇 번째 떨어져있는가를 가리킨다.
배열과 포인터
배열은 포인터다. 따라서 ampersand (address of) 연산자를 붙일 필요가 없다.
char strs[] = {'h', 'e', 'l', 'l', 'o'};
char *ptr = strs; // no ampersand
cout << *(strs+3) << endl;
포인터를 배열처럼 사용하기
cout << *(ptr + 4) << endl;
cout << *(strs + 4) << endl;
cout << ptr[4] << endl;
함수
함수의 반환값은 하나이므로 포인터를 활용하여 함수에서 여러 개의 반환값을 받은 것처럼 사용할 수 있다.
void Add(int a, int b, int *c, int *d)
{
*c = a + b;
*d = a - b;
}
int main()
{
int sum;
int sub;
Add(1,2, &sum, &sub);
cout << sum << endl;
cout << sub << endl;
}
문자열 비교
const
c++ keyword로 변하지 않는 값을 지정한다. 상수라고 한다.
const int kMaxStr = 100; // 상수 전역 변수
함수 IsEqual은 매개변수 str1과 str2를 가진다. 이 때 두 매개변수는 포인터이다. 따라서 배열의 길이를 모른다.
bool IsEqual(const char str1[], const char str2[]);// 동일
bool IsEqual(const char *str1, const char *str2); // 동일
쓰레기값 예외처리
bool IsEqual(const char *str1, const char *str2)
{
for (int i = 0; i < kMaxStr; ++i)
{
if (str1[i] != str2[i])
return false;
if (str1[i] == '\0')
return true;
}
return true;
}
int main()
{
const char str1[kMaxStr] = "stop";
while (1)
{
char input[kMaxStr];
cin >> input; // 남은 공간은 쓰레기값이 들어있다.
if(IsEqual(str1, input))
{
break;
}
}
return 0;
}
동적할당
문자열 복사
서로 다른 포인터에게 아래와 같은 방법으로 해당 주소의 값을 복사하라고 명령할 수 없다.
const int kMaxStr = 100;
int main()
{
char str1[] = "Hello, World!";
char str2[kMaxStr];
str2 = str1; // Error!
}
위의 결과값으로 str2에 str1의 주소값을 넣어주게 된다.
const int kMaxStr = 100;
size_t Min(int a, int b) // sizeof의 결과값은 size_t이다.
{
return a < b ? a : b;
}
int main()
{
char str1[] = "Hello, World!";
char str2[kMaxStr];
// dest, src , 복사할 메모리 크기
memcpy(str2, str1, Min(sizeof(str1), sizeof(str2)));
cout << str2 << endl; // "Hello, World!"
}
정적할당(Static Allocation) VS 동적할당(Dynamic Allocation)
Static Allocation | Dynamic Allocation | |
큰 용량의 저장공간을 할당받을 수 있나? | X | O |
메모리 사용 용량의 변화가 가능한가? | X | O |
동적할당(Dynamic Allocation)
"new" keyword를 사용하여 사용하고 싶은 저장공간의 크기를 할당받는다.
char *dynamic_array = new char[kMaxStr];
OS는 비어있는 공간을 찾아 해당 메모리(저장공간)의 첫 주소를 반환한다.
메모리 해제
사용이 끝난 메모리는 반드시 돌려주자.
메모리 해제에 저장공간의 크기를 설정하지 않는 이유
OS는 이미 알고있다.
delete[] dynamic_array; // 배열 삭제시 []
동적할당을 활용한 문자열 복사
// 문자열 복사
char str1[] = "Hello, World!";
char *dynamic_array = new char[kMaxStr];
// 주의: 동적할당 메모리는 변수 사이즈가 포인터 사이즈임 (배열이 아님)
memcpy(dynamic_array, str1, Min(sizeof(str1), sizeof(kMaxStr)));
// memcpy(dynamic_array, str2, kMaxStr);
cout << dynamic_array << endl;
동적할당 메모리의 주소
동적할당 메모리의 주소값이 크다. 메모리 레이아웃에서 정적할당은 Stack 동적할당은 Heap에 위치한다.
cout << size_t(str1) << endl; // 480006108968
cout << size_t(str2) << endl; // 480006109024
cout << size_t(dynamic_array) << endl; // 2461430842080
해제되지 않은 메모리의 재할당
(1) 메모리 할당을 받고 주소값을 dynamic_array에 저장하였다.
(2) 위 (1)에서 할당받은 동적 메모리를 해제하지 않고 새로운 동적 메모리를 할당받았다.
이 때 (1)에서 할당받은 메모리는 접근할 수 없다. 해제할 수 없다.
따라서 메모리를 동일한 포인터에 재할당 받고 싶다면 기존에 부여받은 동적할당 저장공간을 해제해주자
'Programming Language > C++' 카테고리의 다른 글
[코테용 C++ 핵심정리] 기타 (0) | 2023.11.18 |
---|---|
[코테용C++ 핵심 정리] 챕터5. 구조 만들기 (0) | 2023.11.18 |
[코테용C++ 핵심정리] 챕터3. 흐름제어 (0) | 2023.11.17 |
[코테용C++핵심정리] 챔터2. 기본 개념들 (0) | 2023.11.16 |
[코테용C++ 핵심정리] 챕터1. 작동원리 (0) | 2023.11.16 |