[따배씨++]Chapter6-2. 배열, 포인터, 참조
카테고리: Cpp
태그: Programming
인프런에 있는 홍정모님의 홍정모의 따라하며 배우는 C++ 강의를 듣고 정리한 내용입니다.
공부하는 식빵맘 님의 블로그를 참고했습니다.
🚆 메모리 동적 할당
특징
{ }
범위에 상관 없이 필요할 때 할당받고 사용 후 반납- 메모리 크기를 런타임에 결정 가능
- OS를 거쳐야 해서 조금 느림
- 정적 할당 → stack 메모리 할당 (용량 작음)
- 동적 할당 → heap 메모리 할당 (용량 큼)
- 큰 메모리는 정적 할당(stack memory) 못받음
- 할당받은 메모리 주소를 포인터로 받음
- 반납 까먹으면 memory leak (누수 발생)
할당 - new / 반납 - delete
int* ptr = new int;
*ptr = 7;
int* ptr2 = ptr;
delete ptr;
ptr = nullptr;
cout << ptr << endl; // null
cout << ptr2 << endl; // 원래 ptr 주소
cout << *ptr2 << endl; // 쓰레기 값
- 메모리 할당시(new 선언) 힙 메모리 크기를 기억해 둠
- delete시 해당 주소에 기억해둔 메모리 크기만큼 반납
- delete 해도 포인터 주소는 그대로 존재 (쓰레기 값 들어있음)
- 따라서 delete 후에는 ptr = nullptr 해주는게 좋음
- 간접 참조하는 포인터들 일일히 초기화 하기 힘듬… → 스마트 포인터가 해결해줌
- 주의) delete 하기 전에 nullptr인지 체크하는게 좋음
- nullptr을 delete하는 경우 ‘do nothing’ 이지만 퍼포먼스에 민감하 경우 이야기가 다르다고 함
- nullptr 체크 없이 delete를 하고 싶다면 차라리 스마트 포인터를 쓰는 것이 ‘현대적’ 스타일
동적 할당 배열
int length;
cin >> length;
int* array = new int[length]; // OK 쓰레기 값 들어있음
int* array = new int[length](); // OK 0으로 초기화
int* array = new int[length](1, 2, 3, 4, 5); // Error : 컴파일 타임에 초기화 안하려 함
delete[] array;
- 동적 메모리는 초기화 살짝 까다로움
- 정적 배열과 달리 array로 다른 메모리 영역 가리킬 수 있음
- 사이즈 늘리고 싶은 경우 더 큰 메모리 할당 받아서 내부 값 복사 붙여넣기
- 정적 배열은 이것도 못함
- 보통 벡터 써서 편하게 해결
🚆 포인터와 Const
const + pointer
int a = 5;
int b = 10;
const int* ptr_a = &a;
*ptr_a = 10; // Error
ptr_a = &b; // OK
a = 7; // OK
- de-reference만 불가능
- 다른 건 전부 가능
const int a = 5;
const int* ptr1 = &a; // OK
int* ptr2 = &a; // Error
int* ptr3;
ptr3 = ptr1; // Error
- const value는 const poointer로만 지정 가능
- 일반 포인터는 const 포인터 받을 수 없음
pointer + const
int a = 20;
int b = 5;
int* const ptr = &a;
*ptr = 10; // OK
ptr = &b; // Error
- 포인터가 가리키는 주소값을 못 바꾸게 함
- de-reference는 가능
const int a = 5;
const int* const ptr = &a;
- 아무것도 못 함
🚆 참조 변수 - Reference Variable
int value = 5;
int &ref = value;
int &ref = 123; // Error
int &ref; // Error
- 포인터는 변수의 주소를 담고있음
- 반면 참조는 같은 메모리를 공유! (별명 느낌)
- 초기화 반드시 해야함
- R-value로 초기화 못 함(메모리가 없음)
- const 상수는 const reference만 가능
함수 파라미터로 넘어갈 때
void doSomething(int &n){};
void doSomething(int (&arr)[5]){};
- 변수를 복사해서 넘어가는 것이 아닌 변수 자체가 넘어감
- 포인터도 주소 복사까지는 함
- 퍼포먼스 측면에서도 reference 좋음
- 단 배열을 참조할 때는 사이즈 명시해야 함
구조체(Struct)에서 아주 유용
struct Something
{
int v1;
float v2;
};
struct Other
{
Something st;
};
int main()
{
Other ot;
int& v1 = ot.st.v1;
v1 = 1;
return 0;
}
- 변수 이름이 복잡할 때 참조쓰면 개꿀
참조와 Const
void doSomething(const int& x);
int main()
{
const int x = 5;
int& ref = x; // Error
const int& ref = x; // OK
int& ref2 = ref; // Error
const int& x = 5; // OK
doSomething(10);
return 0;
}
- const 상수는 const 참조만 가능
- 일반 참조는 const 참조 못 받음 (우회해서 바꿀까봐)
- const 참조는 R-value initialize 가능!
- const 참조로 변수 넘겨주면 최적화 가능
포인터와 참조의 멤버 접근
struct Person
{
int age;
double weight;
};
int main()
{
// 일반적인 접근
Person person;
person.age = 5;
// 참조로 접근
Person& ref = person;
ref.age = 15;
// 포인터로 접근
Person* ptr = &person;
ptr->age = 30;
(*ptr).age = 20; // 연산자 우선순위 .이 *보다 높음
return 0;
}
🚆 for-each 반복문
int fibonacci[] = { 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89 };
for (auto number : fibonacci)
number *= 10;
cout << endl;
- 배열 안에 있는 원소들이 한 번씩 돌아감
- 값은 복사해서 들어감
- 값을 수정하고 싶으면 reference!
- 성능 면에서도 reference가 좋음
- 동적 할당은 for-each 사용 불가 → 벡터는 사용 가능
🚆 void 포인터
int i = 5;
float f = 3.0;
char c = 'a';
void* ptr = nullptr;
ptr = &i; // OK
ptr = &f; // OK
ptr = &c; // OK
cout << ptr + 1 << endl; // Error
cout << static_cast<char>(*ptr) << endl; // Error
cout << *static_cast<char*>(ptr) << endl; // OK
- 자료형과 상관없이 포인터 사용 가능
- Generic pointer 라고도 함
- 포인터 연산 불가능
- 자료형 캐스팅해서 써야 함 (*char 한 후 *)
🚆 이중 포인터
int value = 5;
int* ptr = &value;
int** ptrptr = &ptr;
int row, col;
int** matrix = new int* [row];
for (int r = 0; r < row; r++)
matrix[r] = new int[col];
for (int r = 0; r < row; r++)
delete[] matrix[r];
delete[] matrix;
- delete시 순서 주의
🚆 std::array & std::vector
#include <array>
#include <vector>
int main()
{
std::array <int, 5> my_arr = { 1,2,3,4,5 };
std::vector <int> array = { 1,2,3,4, };
return 0;
}
- array : 정적 array 상위호환
- class로 한 번 더 감싸준 형태
- 여러가지 편리한 함수들 내장
- 변수로 넘어가도 사이즈 기억 (포인터로 안 바뀜)
- for-each 사용 가능
- my_arr[10] → 예외처리
- 정적 array는 index 넘어가도 일단 접근은 함 (오류 X)
- vector : 동적 array 상위호환
- 사이즈 자유롭게 변경 가능
- 변수로 넘어가도 길이 기억
- delete 알아서 해줌!
댓글 남기기