
문제 해결을 위해 로직을 구상할 때 큰 틀을 먼저 구상하고 이것에 맞는 자료구조가 뭔지 알아야 한다. 이번에는 아래의 STL 선택 가이드를 참고하여 문제를 풀었다.

연습문제
주어진 단어의 각 문자를 하나씩 뒤로 이동하여 만들어진 모든 회전된 단어를 출력
순서 중요-> 정렬 상태 유지->중간에 지우거나 삽입하지 않음->Persistent Position라고 생각해 List를 반환값으로 골랐다.
list<string> charRotation(string str) {
list<string> charRotation;
for (const auto c : str) {
char c = str[0];
charRotation.push_back(str);
str.push_back(c);
str.erase(0, 1);
}
return charRotation;
}
//
for ( element-declaration : expression )
statement
❗범위기반 for문 사용시 주의할 점
// 범위 기반 for문
for (int n : nums) {
n *= 2; // 원본 수정 불가능
}
// 범위 기반 for문
for (int& n : nums) {
n *= 2; // 원본 수정 가능
}
// 이렇게 변환됨
for (auto it = std::begin(nums); it != std::end(nums); ++it) {
int n = *it; // 복사본 생성
}
범위 기반 for문은 마지막 for문의 원리로 동작하므로 n은 nums요소의 복사본이다. for문에서 해당 배열의 원본을 수정하려면 element를 레퍼런스로 선언해야한다.
int* p : p는 int 변수의 메모리 주소를 저장하는 포인터
1. 변수의 주소값을 담을 수 있다. A = &B; //B의 주소값, A가 포인터일때만 가능
2. 담고 있는 주소값에 해당되는 메모리에 있는 값을 읽거나 수정할 수 있다.
👀👉포인터에 변수 타입이 필요한 이유
주소값을 따라가서 값을 읽으려면 주소를 기준으로 몇 바이트를 읽을 지가 필요해서 어떤 타입 변수의 주소인지가 필요함 ex) int* -> 주소로 가서 4byte만큼 읽음
문자형 포인터 char 은 출력할 때 주의: (void*)ptr 로 출력해야 안깨짐
복사비용을 생각하면 다른 변수에 바로 대입하는거보다 주소값만 들고오는게 나음
역참조연산자: *
*ptr = 10; // ptr이 가리키는 변수의 값을 변경할 수 있다.
배열은 주소값을 가지고 있는 변수. 여러 변수를 묶어서 관리한다.
배열의 이름은 시작 주소를 담고 있어 변경할 수 없다(상수 포인터).
arr:시작 주소값 arr[0]: 시작 주소에 들어있는 변수의 값
arr[i] = *(arr+i)
int arr[3] = {10, 20, 30};
int* p = arr; // 배열의 시작 주소를 포인터에 저장,
//배열의 이름은 배열의 시작 주소를 갖는다. 따라서 주소연산자 필요 없음
*p : p가 가리키는 값
*(p+1) : p+1이 가리키는 값. p는 int* 이기 때문에 +1 이면 4byte씩 증가해서 arr[1]의 주소
arr[1] = *(p + 1) = p[1]: 배열 첨자 연산자는 포인터에서도 쓸 수 있음
배열 포인터: 배열을 가리키는 포인터 int arr[4]; int* ptr = arr;는 크기가 4인 배열 arr를 가리키는 포인터.
2차원 배열쓸 때 사용함
#include <iostream>
using namespace std;
// 배열 포인터: 배열 전체를 가리키는 포인터
int main() {
int arr[3] = { 100, 200, 300 };
int (*ptr)[3] = &arr; // 배열 포인터 선언
// 배열 포인터를 이용하여 배열 요소 접근
cout << "(*ptr)[0]: " << (*ptr)[0] << endl; // 100
cout << "(*ptr)[1]: " << (*ptr)[1] << endl; // 200
cout << "(*ptr)[2]: " << (*ptr)[2] << endl; // 300
return 0;
}
/*
출력 결과:
(*ptr)[0]: 100
(*ptr)[1]: 200
(*ptr)[2]: 300
*/
포인터 배열: 배열의 각 원소가 포인터 int* ptrArr[4];는 크기가 4이고 각 원소가 int*인 배열
#include <iostream>
using namespace std;
// 포인터 배열: 포인터를 원소로 갖는 배열
int main() {
int a = 10, b = 20, c = 30;
int* ptrArr[3] = { &a, &b, &c }; // 포인터 배열 선언 및 초기화
// 포인터 배열을 이용하여 값 출력
cout << "*ptrArr[0]: " << *ptrArr[0] << endl; // 10
cout << "*ptrArr[1]: " << *ptrArr[1] << endl; // 20
cout << "*ptrArr[2]: " << *ptrArr[2] << endl; // 30
return 0;
}
/*
출력 결과:
*ptrArr[0]: 10
*ptrArr[1]: 20
*ptrArr[2]: 30
*/
❗포인터 사용시 주의사항
포인터를 초기화하지 않고 불러오거나 이상한 주소로 역참조를 하는 경우 예기치 못한 에러로 종료된다. 일반 변수 사용할 때보다 리스크가 크므로 조심해서 사용해야 한다.
포인터는 주소값을 직접 다루어야 하므로 복잡해질 수 있는 문제를 해결하기 위해 레퍼런스 문법을 도입했다.
레퍼런스는 특정 변수의 별명이다.
일반 변수와 거의 동일하게 사용할 수 있다. 내부적으로 해당 변수를 직접 가리키는 역할을 한다.
역참조* 없이도 변수의 값을 변경할 수 있는 문법
📢값을 복사하지 않고 대상에 직접 접근하는 안전하고 간결한 방법
int x=3;
int y=x;
y=4; // x=3; x와 y는 메모리가 따로 잡힘
int& z=x;
z=4; // x=4; x와 z는 같은 메모리를 공유
선언과 동시에 초기화해야 한다.
초기화 이후 다른 변수에 연결할 수 없다.
함수 인자 전달 시 복사 비용 줄이기 + 수정 가능
함수에 인자로 일반 변수를 입력할 때 새로운 메모리를 할당받아 값을 복사하여 전달하므로 메모리도 잡아먹고 함수 안에서 값을 변경해도 main()에서는 바뀌지 않음
void change(int& x) {
x = 100;
}
int main() {
int a = 10;
change(a); // a는 100으로 바뀜!
}
함수 리턴값으로 참조 반환
함수 리턴을 참조로 하면 l value로 쓸 수 있음
int& getElement(int arr[], int index) {
return arr[index]; // 참조 반환
}
getElement(arr, 1) = 42; // arr[1] = 42;
nullptr 을 가짐