[1주차] 코딩 테스트 합격자 되기 C++ (코딩테스트 문법) - part 2

2025. 6. 15. 22:44·C++ 코딩테스트 준비

2.  코딩테스트 필수 문법


✅ 빌트인 데이터 타입

C++에서 많이 사용하는 빌트인 데이터 타입은 정수형(int), 부동소수형(float or double), 논리형(bool), 문자형(char)이 있다.

그리고 같은 타입의 기본 자료형 변수들을 연속된 메모리에 저장할 수 있는 배열(array)도 있다.

 

정수형(integer 타입)

정수형(int)은 양의정수, 음의 정수, 0을 포함한다. 크기는 보통 4byte이다. 하지만, 32비트, 64비트 시스템으로 나눈다면 64비트 시스템에서는 4byte or 8byte로도 사용된다. 정수형은 더하기, 빼기, 곱하기, 나누기와 같은 사칙 연산을 수행한다.

 

부동 소수형(float or double)

C++에서 부동소수형은 float와 double이 존재한다. 이 둘의 차이점은 소수점의 자릿수이다. 코드를 보며 이해해 보자.

#include <iostream>

using namespace std;

int main(){
	double d = 2.5;
    float f = 1.5f;
    
    cout << sizeof(d) << endl; // double형의 경우 8byte
    cout << sizeof(f) << endl; // float형의 경우 4byte
	
    return 0;
}

 

위의 코드를 실행하게 되면 double은 8 바이트, float은 4바이트를 사용한다. 그래서 double의 경우는 float보다 유효한 소수점의 자릿수가 더 많다. double은 15자리, float은 7자리로 보면 된다. 코딩테스트에서는 정밀함을 요구하지 않기에 둘 중 하나만 기억해도 좋다.


형 변환(type conversion)

C++을 구현하다 보면 타입을 변경해야 할 필요가 생긴다. 이때 형 변환을 해야 하는데 크게 두 가지 종류가 나뉜다.

 

1. 암시적 형 변환(implicit conversion): 타입이 서로 다른 변수간 연산을 하여 발생하는 경우

 

이 경우 컴파일러가 별도의 지시 없이 자동으로 수행하는 형 변환이다. 

변환 규칙으로는 일반적으로 char -> int -> long -> float -> double 이렇게 작은 타입 -> 큰 타입으로 변환된다.

아래의 코드를 보면 서로 다른 변수를 연산을 할 때 자동으로 수행해 준다.

int a = 10;
double b = 3.14;
double result = a + b;  // int → double로 자동 변환!

 

2. 명시적 형 변환(explicit conversion): 사용자가 임의로 변경하는 경우

 

즉, 이 경우는 프로그래머가 직접 형 변환 연산자를 사용하여 타입을 변환하는 방식이다.

코드를 보면 다음과 같다.

double pi = 3.14159;
int truncated = static_cast<int>(pi);  // 3

C++에 명시적 형 변환 연산자들의 경우 총 4가지가 있다. 

-> static_cast, dynamic_cast, const_cast, reinterpret_cast 


문자열 선언 및 초기화

문자열을 초기화하는 방법은 여러 가지가 있다. 그중 몇 가지를 다뤄보자.

  1. 대입 연산자를 활용하는 방법 (-> 이 방법이 제일 많이 사용)
  2. 생성자로 다른 문자열을 복사
  3. 특정 문자를 반복한 패턴의 문자열 생성
#include <iostream>
#include <string>

using namespace std;

int main() {
	
    string str1; // 빈 문자열 선언
    string str2 = "Hello, World!"; // 문자열 직접 초기화
    string str3(str2); // 문자열 복사
    string str4(str2, 0, 5); // 문자열 부분 복사 초기화 = Hello
    string str5(10, '*'); // 반복된 문자로 문자열 초기화 = **********

	return 0;
}

문자열 찾기

특정 문자나 문자열을 찾을 때 find() 메서드를 사용한다. 방법은 다음과 같다.

  • find(찾으려는 문자열)
  • find(찾으려는 문자열, 탐색 시작 위치)

find() 메서드는 문자열을 찾으면 해당 문자열이 시작하는 인덱스를 반환하고, 찾지 못하면 string::npos를 반환한다. 그리고 탐색하는 문자열의 길이가 N일 때 find() 메서드는 O(N)으로 동작한다.

 

직접 코드를 다뤄보자.

#include <iostream>
#include <string>

using namespace std;

int main() {

    // 문자열 초기화
    string str = "Hello, C++ World!";
    
    // "Hello" 문자열 찾기
    size_t pos1 = str.find("Hello");
    cout << pos1 << endl; // 문자열의 시작 위치인 0 반환
    
    // 'C' 문자 찾기
    size_t pos2 = str.find('C');
    cout << pos2 << endl; // 문자열의 시작 위치인 7 반환
    
    // "Hello" 문자열 찾기, 시작 인덱스 지정
    size_t start_index = 2;
    size_t pos3 = str.find("Hello", start_index);
    cout << pos3 << endl; // 18446744073709551615 = string::npos값, 상황에 따라 값 다름.
    // 이 경우는 탐색 시작 위치가 2이므로 Hello를 찾지 못하게 되기 때문이다.
    
    // 존재하지 않는 문자열 찾기
    size_t pos4 = str.find("Python");
    cout << pos4 << endl; 18446744073709551615 = string::npos값, Python을 찾지 못함.

}

문자열 추가, 수정

문자열 추가는 '+' 연산자 혹은 '+=' 연산자를 사용한다. 특정 문자를 수정하려면 '[ ]' 연산자를 활용해서 임의 접근 후 수정하거나 replace() 메서드를 활용한다.

  • 사용법: replace(시작위치, 시작 위치부터 몇 개의 문자열을 대체할 것인지, 대체할 문자열)

즉, 첫 번째 인수와 두 번째 인수로 주어진 범위 내 문자열이 세 번째 인수로 받은 문자열로 대체된다. replace()의 경우 대체할 문자열의 길이가 N일 때 O(N)이다. 다음의 코드를 보며 이해해 보자.

#include <iostream>
#include <string>

using namespace std;

int main() {
    
    string str = "APPLE";
    str += ", World!"; // 문자열 추가 연산자 사용
    cout << str << endl; // "Apple, World!" 출력
    
    //문자열 수정
    str[7] = 'P'; // 7번째 문자 W -> P로 수정
    cout << str << endl; // "Apple, Porld!" 출력
    
    str.replace(7, 4, "Col"); // 7번째 문자부터 4개의 문자를 Col로 변경
    cout << str << endl; // "Apple, Cold!" 출력

	return 0;
}

 


✅ STL

STL은 C++에서 제공하는 템플릿 기반 표준 라이브러리이다. 코딩테스트에서 많이 사용하고 필수적으로 알고 있어야 한다.

STL은 크게 컨테이너, 알고리즘, 반복자로 이루어져 있다.

 

1. auto문

타입이 복잡해질 경우 사용할 때 실수하기 쉽고 코드도 길어지므로 가독성이 떨어질 수 있다. 

이때 auto문을 사용해서 변수의 타입을 자동으로 추론하도록 해준다. 코드를 통해 보면 이해하기 쉽다. 다음을 보자.

auto num = 42; // int로 추론

auto pi = 3.14159; // double로 추론

auto greeting = string("Hello, C++"); // string으로 추론

이와 같이 오른쪽 변수가 무엇이냐에 따라 자동으로 추론해 준다.


2. 범위 기반 반복문

배열이나 컨테이너의 모든 원소를 순화할 때 사용한다. 기본 반복문보다 구현이 더 쉽고 가독성이 좋다.

사용법은 다음과 같다.

// 사용 방법
for(타입 변수명 : 컨테이너) {
 // 코드
}

 

이해를 돕기 위해 실제 코드 예시를 보자. vector, map, set에 대한 범위 기반 반복문 예시이다.

#include <iostream>
#include <vector>
#include <map>
#include <set>

using namespace std;

int main() {
 
    // vector 예시
    vector<int> vec = {1, 2, 3, 4, 5};
    for (int num : vec) {
        cout << num << " "; // 1 2 3 4 5 출력
    }
    
    // map 예시
    map<string, int> fruitMap = {{"apple", 1}, {"banana", 2}, {"cherry", 3}};
    for (const auto& pair : fruitMap) {
    	cout << pair.first << "=" << pair.second << " "; // apple = 1 banana = 2 cherry 3 출력
    }
    
    // set 예시
    set<string> fruitSet = {"apple", "banana", "cherry"};
    for (const auto& fruit : fruitSet) {
    	cout << fruit << " "; // apple banana cherry
    }
    
 	return 0;
 }

STL 컨테이너와 사용할 때는 값을 수정할 필요가 없기에 auto 대신 상수 레퍼런스를 적용한 const auto&를 사용했다. 

수정해야 할 때는 레퍼런스를, 수정하지 않아야 할 때는 상수 레퍼런스 적용을 고려해 보자.


3. 반복자(iterator)

반복자는 C++에서 컨테이너(벡터, 셋, 맵 등)의 종류와 관계없이 원소들을 순회하고 접근할 수 있게 해 준다.

코딩테스트에 꼭 필요한 순방향 반복자와 역방향 반복자에 대해 알아보자.

 

1. 순방향 반복자: 컨테이너의 원소를 순차적으로 순회할 수 있게 해 준다.

  • 시작 위치 begin()과 끝 위치 end()가 쌍으로 나오는 경우가 많다.
  • begin()부터 end() 바로 직전까지 순회하라는 의미
  • 특정 원소를 탐색하거나 조건에 맞는 원소를 찾는 경우에는 해당 원소를 찾으면 위치를 반환하고, 그렇지 않으면 end()를 반환한다.

다음은 벡터에서의 순방향 반복자를 사용하는 코드이다. 

vector<int> vec = {10, 20, 30, 40, 50};

for(auto it = vec.begin(); it != vec.end(); ++it){
	cout << *it << " "; // 10 20 30 40 50 출력
}

it != vec.end()로 왜 했는지 좀 이해가 안 갈 수 있다. end()는 아까도 말했지만 end() 바로 직전까지 순회하라는 것이다.
아래 그림으로 보면 이해가 쉽다.

벡터: [10] [20] [30] [40] [50] [end()]
          ↑                                        ↑
       begin()                               end()

 

2. 역방향 반복자: 말 그대로 순방향 반복자와 방향이 반대이다. 다른 점으로는 ++ 연산자를 사용할 때 순방향 반복자는 다음 원소로 위치가 바뀌지만, 역방향 반복자는 이전 원소로 위치가 바뀌다는 것이다. 사용법으로는 rbegin()과 rend()를 사용한다. 아마 reverse의 r이지 않을까 싶다. rbegin()은 맨 마지막 원소의 위치이고 rend()는 맨 처음 원소의 바로 직전 위치이다. 즉, 방향만 다르고 원리는 같다.

그럼 이번에도 코드를 살펴보면, 그저 반대로 출력하는 것일 뿐 다른 것은 없다.

vector<int> vec = {10, 20, 30, 40, 50};

for(auto it = vec.rbegin(); it != vec.rend(); ++it){
	cout << *it << " "; // 50 40 30 20 10 출력
}

✅ STL의 컨테이너

STL의 컨테이너는 데이터를 저장하는 객체이다. 실제로는 더 많은 컨테이너를 지원하지만, 주로 알아야 할 것은 벡터, 셋, 맵, 우선순위 큐이다. 하지만 이것을 어느 상황에 써야 하는가?

데이터 접근 방식에 따른 컨테이너 선택을 해야 한다.

  • 배열처럼 인덱스로 접근 → 벡터(vector)
  • 키-값 쌍으로 저장 → 맵(map)
  • 중복 없는 데이터 집합 → 셋(set)

1. 벡터(vector)

벡터는 크기가 자동으로 조절되는 배열입니다. 인덱스로 빠른 접근이 가능하고, 끝에서의 삽입/삭제가 매우 효율적이다.

 

- 벡터의 선언 및 초기화

#include <vector>
using namespace std;

// 다양한 초기화 방법
vector<int> vec1;                    // 빈 벡터
vector<int> vec2(5);                 // 크기 5, 모든 값은 0
vector<int> vec3(5, 10);             // 크기 5, 모든 값은 10
vector<int> vec4 = {1, 2, 3, 4, 5};  // 초기값 지정

 

 

- 벡터 삽입과 삭제

벡터의 내부는 배열로 구성되어 있다. 따라서 맨 뒤에서는 삽입, 삭제 연산을 효율적으로 가능하지만, 맨 앞에서 삽입, 삭제할 경우에는 매우 비효율적이다. 왜냐하면 맨 앞의 원소를 삽입, 삭제하면 뒤의 원소들을 한 칸씩 이동해야 하기 때문이다.

  • 맨 뒤의 원소를 삽입: push_back() 
  • 맨 뒤에 있는 원소를 삭제: pop_back()
vector<int> vec = {10, 20, 30};

// 삽입
vec.push_back(40);                    // 끝에 추가: {10, 20, 30, 40}
vec.insert(vec.begin() + 1, 15);      // 인덱스 1에 15 삽입: {10, 15, 20, 30, 40}

// 삭제
vec.pop_back();                       // 마지막 원소 삭제: {10, 15, 20, 30}
vec.erase(vec.begin() + 1);           // 인덱스 1 원소 삭제: {10, 20, 30}

// 유용한 함수들
cout << vec.size() << endl;           // 크기 출력
cout << vec.front() << endl;          // 첫 번째 원소: 10
cout << vec.back() << endl;           // 마지막 원소: 30

2. 셋(set)

셋은 중복을 허용하지 않고, 저장된 데이터를 자동으로 정렬하는 컨테이너이다.

 

- 셋의 선언 및 초기화

#include <set>
using namespace std;

set<int> s1;                          // 빈 셋
set<int> s2 = {3, 1, 4, 1, 5};        // 초기값 지정, 중복값 제거되고 자동 정렬: {1, 3, 4, 5}
set<string> fruits = {"apple", "banana", "cherry"};

 

- 셋의 원소 탐색

셋에 특정 원소가 있는지 확인하려면 find() 메서드를 사용한다. 시간 복잡도는 셋의 크기가 N일 때 O(logN)이다.

찾는 원소가 있다면 그 원소의 위치를 반환하고 없으면 end 반복자를 반환한다.

set<int> numbers = {1, 3, 5, 7, 9};

// find() 함수 사용
auto it = numbers.find(5);
if(it != numbers.end()) {
    cout << "5를 찾았습니다!" << endl;
} else {
    cout << "5가 없습니다." << endl;
}

 

 

- 셋의 삽입과 삭제

셋은 모든 삽입, 삭제의 시간 복잡도는 O(logN)이다. 그 이유는 정렬된 상태를 유지하기 때문이다. 

  • 삽입: insert()
  • 삭제: erase()
set<int> s = {1, 3, 5};

// 삽입
s.insert(4);                          // {1, 3, 4, 5}
s.insert(3);                          // 중복이므로 변화 없음

// 삭제
s.erase(3);                           // 값으로 삭제: {1, 4, 5}
auto it = s.find(4);     	          // 원소 4가 있는지 확인
s.erase(it);                          // 반복자로 삭제: {1, 5}

3. 맵

맵은 키와 값을 쌍으로 갖는 컨테이너이다. 여기서 키와 값의 쌍을 entry, STL에서는 std::pair타입으로 표현한다.

내부는 균형 이진 탐색 트리로 구성되어 있기 때문에 항상 키값을 기준으로 데이터가 자동 정렬된다. 시간 복잡도는 N개의 키가 있다면 키를 지준으로 검색, 삽입, 삭제 연산 시 O(logN)이다. 그리고 값으로 말고 키 자체를 통해 원하는 값을 찾을 수 있다.

- 맵의 선언 및 초기화

#include <map>
using namespace std;

map<string, int> ages; // 빈 맵
map<string, int> scores = {
    {"Alice", 95},
    {"Bob", 87},
    {"Charlie", 92}
};

// 2차원 맵도 가능하다.
map<string, map<string, int>> student_grades;

- 맵에서 특정 키 접근

맵에 특정 키에 접근하는 방법은 2가지이다.

  • [ ] 연산자 활용
  • find() 메서드 활용
map<string, int> ages = {{"Alice", 25}, {"Bob", 30}};

// [] 연산자 사용
cout << ages["Alice"] << endl;        // 25 출력

// find() 함수로 안전하게 접근
auto it = ages.find("Charlie");
if(it != ages.end()) {
    cout << it->second << endl;
} else {
    cout << "Charlie를 찾을 수 없습니다." << endl;
}

// 찾아보니 C++ 11부터 at도 사용한다고 한다.
// at() 함수 사용 (안전한 접근)
cout << ages.at("Bob") << endl;       // 30 출력

 

 

- 맵의 삽입과 삭제

삽입하는 방법은 2가지이다. 

  • insert() 메서드 -> 이 경우 make_pair() 함수, { }를 사용
  • [ ] 연산자 활용
map<string, int> inventory;

// 삽입
inventory["sword"] = 5;               // [] 연산자로 삽입
inventory.insert({"shield", 3});      // insert()에서 {}로 삽입
inventory.insert(make_pair("potion", 10));  // make_pair로 삽입

// 삭제
inventory.erase("shield");            // 키로 삭제
auto it = inventory.find("potion");
inventory.erase(it);                  // 반복자로 삭제

 

- 정렬되지 않은 셋과 맵

문제를 풀 때 굳이 필요 없는데도 정렬을 하게 되면 성능 저하를 가져온다.

그렇기 때문에 이때 사용할 수 있는 unordered_set과 unordered_map을 제공한다.

기본 정렬되는 셋과 맵과 동일하지만 해시 기반이며 데이터가 자동 정렬되지 않기 때문에 시간복잡도는 O(1)로 성능이 더 빠르다.

#include <unordered_set>
#include <unordered_map>

unordered_set<int> fast_set = {1, 3, 5, 7};
unordered_map<string, int> fast_map = {{"key1", 10}, {"key2", 20}};

 


✅ STL의 알고리즘

- count() 함수

count() 함수는 말 그대로 횟수를 세는 함수이다. 컨테이너 내에서 특정 값이 나타나는 횟수를 세는 것이다.

  • count(시작 반복자, 끝 반복자,  횟수를 세고 싶은 값)
#include <algorithm>
#include <vector>
using namespace std;

vector<int> numbers = {1, 2, 3, 2, 4, 2, 5};

// 값 2가 몇 개인지 세기
int count_2 = count(numbers.begin(), numbers.end(), 2);
cout << "2의 개수: " << count_2 << endl;  // 3개

// 문자열에서도 사용 가능
string text = "hello world";
int count_l = count(text.begin(), text.end(), 'l');
cout << "l의 개수: " << count_l << endl;  // 3개

 

- sort() 함수

이 함수도 이름과 같이 정렬하는 함수다. 

  • sort(시작 반복자, 끝 반복자)
  • sort(시작 바복자, 끝 반복자, 비교 함수)
#include <algorithm>
#include <vector>

using namespace std;

vector<int> nums = {5, 2, 8, 1, 9};

// 오름차순 정렬
sort(nums.begin(), nums.end());
// 출력: {1, 2, 5, 8, 9}

// 내림차순 정렬
sort(nums.rbegin(), nums.rend());
// 출력: {9, 8, 5, 2, 1}

 

- next_permutation() 함수

이 함수는 가능한 모든 순열을 생성한다.

  • next_permutation(시작 반복자, 끝 반복자)

가능한 순열이 있다면 true, 없다면 false 반환

#include <algorithm>
#include <vector>

using namespace std;

int main() {
    vector<int> nums = {1, 2, 3};

    cout << "모든 순열:" << endl;
    do {
        for(int num : nums) {
            cout << num << " ";
        }
        cout << endl;
    } while(next_permutation(nums.begin(), nums.end()));

    return 0; 

/* 출력값:
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1
*/

}

 

- unique() 함수

unique() 함수는 말 그대로 유니크하다. 즉,  컨테이너 내 중복되는 원소들을 뒤로 밀어내고 중복되지 않은 원소들만 남겨 새로운 범위의 끝 반복자를 반환한다.

  • unique(시작 반복자, 끝 반복자)
#include <algorithm>
#include <vector>
using namespace std;

int main() {
    vector<int> nums = {1, 1, 2, 2, 2, 3, 3, 4};

    // unique() 함수 적용하여 중복 요소 제거
    auto newEnd = unique(nums.begin(), nums.end());

    // 중복되지 않는 요소들만 출력
    for(auto it = nums.begin(); it != newEnd; ++it) {
        cout << *it << " ";
    }
    cout << endl;
    // 출력: 1, 2, 3, 4, 5

    return 0;
}

 

 

 

- binary_search 함수

#include <algorithm>
#include <iostream>
#include <vector>

using namespace std;

int main() {
	vector<int> nums = {1, 2, 3, 4, 5};
    
    cout << binary_search(nums.begin(), nums.end(), 3) << endl; // 3은 컨테이너에 있는 값으로 1을 반환
    cout << binary_search(nums.begin(), nums.end(), 7) << endl; // 7은 없으므로 0 반환

	return 0;
}

 

- max_element(), min_element() 함수

각각 최댓값, 최솟값의 위치를 반환하는 함수이다.

#include <algorithm>
#include <vector>
using namespace std;

int main() {

    vector<int> nums = {1, 3, 5, 7, 2, 4, 6};
    
    auto maxIt = max_element(nums.begin(), nums.end());
    auto minIt = min_element(nums.begin(), nums.end());
    
    cout << *maxIt << endl; // nums 벡터에 7이 제일 크므로, 7 출력
    cout << *minIt << endl; // 1이 제일 작으므로, 1 출력
    
	return 0;
}

[출처] 코딩 테스트 합격자 되기 C++ 편 - 저자 박경록에서 인용

'C++ 코딩테스트 준비' 카테고리의 다른 글

[2주차] 코딩 테스트 합격자 되기 C++ (배열)  (0) 2025.06.22
[1주차] 코딩 테스트 합격자 되기 C++ (코딩테스트 문법) - part 1  (7) 2025.06.14
'C++ 코딩테스트 준비' 카테고리의 다른 글
  • [2주차] 코딩 테스트 합격자 되기 C++ (배열)
  • [1주차] 코딩 테스트 합격자 되기 C++ (코딩테스트 문법) - part 1
탱도시락
탱도시락
  • 탱도시락
    코딩도시락
    탱도시락
  • 전체
    오늘
    어제
    • 분류 전체보기 (25)
      • CS50 (4)
      • 항해99 코테 (17)
      • C++ 코딩테스트 준비 (3)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    항해99
    CS50
    코딩테스트
    2차원 배열
    코딩테스트준비
    컴퓨터과학
    2진수
    코딩테스트 문법
    부스트코스
    코테
    Til
    Vector
    C++
    2진법
    아스키코드
    James Cameron
    데이터 타입
    시간복잡도
    인간도 모델이다
    코딩테스트 합격자 되기
    유니코드
    창작과모방
    99클럽
    CS50 코칭스터디 2기
    컴퓨팅 사고력
    디지털윤리
    알고리즘 효율 분석
    개발자취업
    코칭스터디 2기
    배열의 효율성
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
탱도시락
[1주차] 코딩 테스트 합격자 되기 C++ (코딩테스트 문법) - part 2
상단으로

티스토리툴바