C++ 학습 시 자주 사용되는 핵심 개념들을 코드 예제와 함께 정리합니다.
기본 입출력 및 배열 초기화
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 110;
int d[N]; // 전역 변수
int main() {
int n;
cin >> n;
cout << n * 2 << endl;
// memset을 사용한 배열 초기화
memset(d, -1, sizeof(d)); // d 배열의 모든 요소를 -1로 설정
// 데이터가 100만 개 이상이면 scanf 사용 고려
// scanf("%d", &n);
return 0;
}
팁: memset은 바이트 단위 초기화이므로 0이나 -1 이외의 값에는 사용에 주의해야 합니다. 대규모 입력 처리 시 scanf/printf가 cin/cout보다 빠릅니다.
포인터 심층 분석
기본
포인터 변수는 메모리 주소를 저장합니다. 타입과 관계없이 포인터 변수 자체의 크기는 동일합니다(예: 64비트 시스템에서는 8바이트).
int a = 10;
int* p = &a; // p는 a의 주소를 저장
const와 포인터 1: 가리키는 값을 수정하지 못하게 하기
const double pi = 3.14;
double* ptr = π // 오류: const 변수의 주소를 비const 포인터에 할당 불가
const double* cptr = π // 정상
double f = 3.14;
const double* cp = &f; // 정상: 비const 객체의 주소를 const 포인터에 할당 가능
// *cp = 1.68; // 오류: const 포인터를 통해 값 수정 불가
f = 1.68; // 정상: f 자체는 const가 아니므로 직접 수정 가능
// 함수 매개변수로 활용
void process(const int* p) {
// *p = 10; // 오류: 수정 불가
cout << *p << endl;
}
요약: const T* 형태는 "포인터가 가리키는 대상"을 상수 취급하여 간접 수정을 막습니다.
const와 포인터 2: 포인터 자체를 상수화하기
int a = 10, b = 20;
int* const pc = &a; // pc는 상수 포인터 (주소 변경 불가)
// pc = &b; // 오류: pc가 가리키는 주소는 읽기 전용
// pc++; // 오류
*pc = 100; // 정상: pc가 가리키는 대상(a)은 수정 가능
문자열 처리 (string)
<string>과 <cctype>을 포함하여 사용합니다.
#include <string>
#include <cctype>
#include <algorithm>
using namespace std;
int main() {
string s1 = "Hello";
string s2 = "World";
s1 += " "; // "Hello "
s1.append(s2); // "Hello World"
// 서브스트링 & 검색
string sub = s1.substr(6, 5); // "World"
size_t pos = s1.find("World"); // 6
// 삽입 & 삭제
s1.insert(5, ","); // "Hello, World"
s1.erase(5, 1); // "Hello World" (쉼표 제거)
// 비교
if (s1 == "Hello World") { }
int cmp = s1.compare("Apple"); // 양수 반환
// 대소문자 판별 & 변환
char c = 'g';
if (isalpha(c)) {
char upper = toupper(c); // 'G'
char lower = tolower(c); // 'g'
}
// 정렬
string s = "cdefba";
sort(s.begin(), s.end()); // "abcdef"
return 0;
}
STL 컨테이너
vector
#include <vector>
#include <algorithm>
// 1차원
std::vector<int> v1(5); // {0,0,0,0,0}
std::vector<int> v2(5, 10); // {10,10,10,10,10}
v1.push_back(7); // 끝에 추가
v1.pop_back(); // 마지막 요소 제거
sort(v1.begin(), v1.end()); // 정렬
reverse(v1.begin(), v1.end()); // 뒤집기
// 2차원
int rows = 3, cols = 4;
vector<vector<int>> mat(rows, vector<int>(cols, 0));
int m = mat.size(); // 행 개수
int n = mat[0].size(); // 열 개수
map & set
#include <map>
#include <set>
// set 기본
set<int> s;
s.insert(10);
s.erase(10);
if (s.find(10) != s.end()) { }
// map 기본
map<string, int> m;
m["apple"] = 5;
m.insert({"banana", 10});
auto it = m.find("apple");
if (it != m.end()) {
string key = it->first; // "apple"
int val = it->second; // 5 (수정 가능)
it->second = 3; // 값 변경 가능
}
// set: 중복 제거, 존재 여부 확인
// map: 빈도수 계산, 키-값 매핑
// unordered_map: O(1) 평균 조회 (해시 테이블)
pair
#include <utility> // 대부분의 STL 헤더가 간접 포함
pair<int, string> p1(21, "hello");
vector<pair<int, string>> vec;
vec.push_back(make_pair(42, "world"));
stack & queue
#include <stack>
#include <queue>
// stack
stack<int> st;
st.push(1);
int top = st.top(); // 1
st.pop(); // 제거 (반환값 없음)
// queue
queue<int> q;
q.push(1);
int front = q.front();
int back = q.back();
q.pop();
구조체 (struct)
생성자와 초기화
// 생성자 없음 - 중괄호 초기화
struct Point {
int x, y;
};
Point p1 = {1, 2};
// 생성자 있음 - 괄호 초기화
struct Data {
int id;
double val;
Data(int i, double v) : id(i), val(v) {}
};
Data d1(10, 3.14);
Data* pd = new Data(20, 2.71);
// 구조체 배열
struct Range {
int l, r;
Range(int left, int right) : l(left), r(right) {}
bool operator<(const Range& other) const {
return r < other.r; // 끝점 기준 정렬
}
};
Range arr[3] = {
Range(1, 5),
Range(2, 3),
Range(4, 6)
};
sort(arr, arr + 3); // operator< 사용
sort를 위한 비교 함수
// 외부 비교 함수
bool cmp(const pair<int, int>& a, const pair<int, int>& b) {
if (a.first != b.first) return a.first < b.first;
return a.second > b.second;
}
vector<pair<int, int>> vec;
sort(vec.begin(), vec.end(), cmp);
// 구조체 내부 연산자 오버로딩
struct Item {
int first, second;
bool operator<(const Item& o) const {
if (first != o.first) return first < o.first;
return second > o.second;
}
};
vector<Item> items;
sort(items.begin(), items.end());
priority_queue
#include <queue>
// 기본: 최대 힙 (max-heap)
priority_queue<int> maxHeap;
// 최소 힙 (min-heap)
priority_queue<int, vector<int>, greater<int>> minHeap;
// 기본 연산
minHeap.push(10);
minHeap.push(5);
minHeap.push(20);
cout << minHeap.top(); // 5 (최소값)
minHeap.pop(); // 5 제거
cout << minHeap.top(); // 10
이터레이터 (Iterator)
#include <vector>
#include <iostream>
vector<int> v = {1, 2, 3, 4, 5};
// 순방향
for (auto it = v.begin(); it != v.end(); ++it) {
cout << *it << " "; // 1 2 3 4 5
}
// 역방향 (rbegin: 마지막 요소, rend: 첫 요소 이전)
for (auto rit = v.rbegin(); rit != v.rend(); ++rit) {
cout << *rit << " "; // 5 4 3 2 1
}
이터레이터 vs. 포인터 vs. 참조
- 포인터: 메모리 주소를 저장,
*로 역참조, 산술 연산 가능. - 참조: 별명(alias), 선언 시 반드시 초기화, 재할당 불가, 널(null) 불가.
- 이터레이터: 컨테이너 요소 접근을 위한 추상화; 포인터와 유사한 문법(
*it,++it) 지원.