PS 공부를 위한 C++ 사용법을 개인 학습 목적으로 일부 정리한 것이므로 다소 부정확한 설명이 포함되어 있을 수 있음.
PS가 아닌 C++ 언어를 학습할 목적이라면 이런 것 말고 다른 전문성 있는 자료들을 추천.
// main.cpp
#include <iostream>
먼저 입출력을 위한 라이브러리를 불러온다.
아래 코드는 방금 include 한 iostream.cpp 파일의 일부를 자른 것이다.
71줄 짜리 코드인데 C++는 한참 전에 나온 원시적인 프로그래밍 언어이므로 이 코드를 그냥 복사해서 main.cpp에 붙여넣어 준다고 보면 된다.
// -*- C++ -*-
//===--------------------------- iostream ---------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#ifndef _LIBCPP_IOSTREAM
#define _LIBCPP_IOSTREAM
/*
iostream synopsis
#include <ios>
#include <istream>
#include <ostream>
#include <streambuf>
namespace std {
extern istream cin;
extern ostream cout;
extern ostream cerr;
extern ostream clog;
extern wistream wcin;
extern wostream wcout;
extern wostream wcerr;
extern wostream wclog;
} // std
// 이후 코드는 생략

std는 Java의 패키지랑 비슷하다고 생각하면 되는데 C++에서는 namespace라고 부른다.
std 패키지 밑에 있는 cout를 std::cout와 같이 사용할 수 있다.
// main.cpp
#include <iostream>
int main()
{
std::cout << 1 << '\n';
return 0;
}
아래와 같이 using namespace std;를 써주면 std에 속한 함수, 클래스 등을 namespace 없이 사용할 수 있다.
// main.cpp
#include <iostream>
using namespace std;
int main()
{
cout << 1 << '\n';
return 0;
}
cout 쪽을 향하는 << 연산자를 사용해서 원하는 만큼 문자, 숫자 가리지 않고 다 출력할 수 있다.
'1' + '\n'이 출력된다고 생각해도 될 것이다.
(참고로 C++ true, false는 그냥 숫자 1, 0이므로 둘 중 하나 출력됨)
<<는 원래 비트 쉬프트 연산자다.
동작이 다른건 당연히 연산자 오버로딩을 해서 동작을 바꾼 것일 것이다.
(PS 목적이면 방향만 잘 알아두자 <<가 출력이고 >>은 입력이다)
이제 코드를 실행하면 1이 출력될 것이다.
g++ -std=c++20 main.cpp -o main.out
./main.out
위와 같이 컴파일 후 실행해도 되지만 누구나 귀찮을 것이기 때문에 VSCode를 사용한다면 Code Runner를 설치해서 단축키로 한 방에 실행하도록 하자.
이걸 설치하고

Cmd + Shift + P(윈도우는 Cmd 말고 Ctrl)을 눌러서 short 치고 들어간다.

"code-runner.run"을 원하는 단축키로 설정해준다.

이제 단축키로 편하게 실행할 수 있을 것이다.
cout을 cin으로 바꿔주고 <<를 돌려서 >>로 써주면 된다.
추가로 입력 받을 빈 변수를 하나 써줘야겠다.
int 하나 입력을 받아보자
// main.cpp
#include <iostream>
using namespace std;
int main()
{
int a;
cin >> a;
cout << a << '\n';
return 0;
}
1
1
1을 넣으면 당연히 1이 출력될 것이다.
1 1
1
공백으로 구분해서 숫자 2개를 넣어도 하나만 가져온다.
`
0
이상한 문자를 넣으면 0이 나온다.
C++에서 지역 변수를 초기화 하지 않으면 이전에 다른 변수 등에 의해 사용되던 쓰레기값이 그대로 남아있다.
그래서 아래 코드는 이상한 값이 출력될 것이다.
#include <iostream>
using namespace std;
int main()
{
int a;
cout << a << '\n';
return 0;
}
그런데 전역 변수는 알아서 비트패턴 0으로 초기화해준다.
그래서 항상 0이 출력될 것이다.
#include <iostream>
using namespace std;
int a;
int main()
{
cout << a << '\n';
return 0;
}
실무에서 코드를 작성한다면 전역변수의 사용은 인생이 망하는 지름길이지만,
PS에서는 쓰레기 값으로 인한 오류를 방지하기 위해 전역변수를 사용하라고 권장하더라.
백준에서 임의의 문제를 찾아 몇가지 입력 패턴을 연습해보고 마무리하겠다.
입력만 받아보자.
5 20
4 42 40 26 46
두 줄이고 첫 줄은 한 줄에 숫자 2개
둘째 줄은 최대 숫자 100만개까지 들어올 수 있다고 한다.
#include <iostream>
using namespace std;
int N, M;
int buf[1_000_999];
int main()
{
cin >> N >> M; // 첫째 줄에 공백으로 구분된 숫자 2개를 받음
for (int i = 0; i < N; i++) {
// N개만큼 쭉 받아옴(한줄)
cin >> buf[i];
}
cout << a << '\n';
return 0;
}
여기서는 두번째 줄에 받아올 수의 개수를 N으로 줬기 때문에 이런식으로
100만 개짜리 buf 배열을 선언하고, for문 돌려서 하나씩 넣어주면 된다.
cin이 알아서 공백, '\n', '\t' 등의 ascii space 문자 단위로 잘라서 변수에 넣어주기 때문이다.
참고로 배열 크기가 1_000_999인 것은 가끔 크기 세팅을 미세하게 잘못해서 오류가 나는 경우가 있기 때문에 쓸데없는 오류 때문에 시간을 날리는 경우를 몇 번 겪고 그것을 방지하기 위함이다.
999에 별 의미는 없다.
역시 입력만 받을 것이다.
3
ababa
aabba
abcaa
2
ababa
abbaa
이런 식으로 처음에 숫자 3이 들어오고 3줄만큼 문자열이 들어온다.
그 뒤에 숫자 하나가 더 들어오고 그만큼 문자열이 더 들어온다.
어차피 같은 것의 반복이니
3
ababa
aabba
abcaa
이것만 해보자.
#include <iostream>
#include <vector>
using namespace std;
string buf;
int N;
vector<string> V;
int main()
{
cin >> N; // 1. 숫자 N 받아옴
for (int i = 0; i < N; i++)
{
// 임시로 문자열 받아옴
cin >> buf;
// 그걸 vector에 넣어줌
V.push_back(str);
}
return 0;
}
vector은 Java의 ArrayList같은 동적 배열이다.
Java를 안해봤긴 한데 아마 맞을 것이다.
Go에서는 Slice라고 부른다.
잘 모르겠으면 배열은 크기가 고정인데 vector는 크기가 늘어나거나 줄어들 수 있어서 동적 배열이라고 부른다고만 알아두자.
여하튼 방금 말했듯 동적으로 크기가 늘어날 수 있기 때문에 이렇게 몇 개가 들어올지 모르는 상황에서는 index에 직접 접근해서 값을 변경하지 않고 vector에 push_back으로 맨 끝에 하나씩 값을 추가해주는 방법도 있다.
vector를 사용하기 위해서 #include <vector>를 포함해야한다.
한 줄에 공백이 포함된 문자열을 #이 나올 때까지 입력 받는 문제다.
>입력
<body> <strong>Oops, this is</body> naughty </strong>
<body> <strong>Hello</strong> <br /> </body>
Just text, no tags.
<p> Oh dear, we are missing something.
<a href="http://www.nzprogcontest.org.nz">This is a link</a>
#
cin은 공백에 따라 입력값을 나누기 때문에
첫 줄이 <body>, <string>Oops,, this, is</body> ... 이런 식으로 잘리게 될 것이다.
공백을 포함한 한 줄을 모두 하나의 string 변수에 담고 싶으면 getline을 사용하면 된다.
#include <iostream>
#include <vector>
using namespace std;
vector<string> V;
string str;
int main()
{
while (1) // 입력
{
getline(cin, str);
if (str == "#")
break;
V.push_back(str);
}
// 확인을 위한 출력
cout << "============\n";
for (auto s : V)
{
cout << s << '\n';
}
return 0;
}
전역 변수로 vector<string> V, string str을 선언(암시적으로 비트 패턴 0으로 초기화 됨)
그리고 while 문 내에서 string 한 줄을 입력 받을 때마다 str에 저장하고, 그 값을 V에 추가한다.
입력이 "#"인 경우 추가하지 않고 while을 종료한다.
그 뒤 V에 저장된 string들이 제대로 들어갔는지 출력을 통해 확인하면 잘 들어간 것을 볼 수 있다.
// 출력
============
<body> <strong>Oops, this is</body> naughty </strong>
<body> <strong>Hello</strong> <br /> </body>
Just text, no tags.
<p> Oh dear, we are missing something.
<a href="http://www.nzprogcontest.org.nz">This is a link</a>
보통은 입력 받을 단어, 수 등이 몇개인지가 변수로 주어지는데, 가끔은 그걸 안 알려줄 때가 있다.
그럴 때는 입력의 끝을 나타내는 EOF를 만나면 종료되도록 코드를 짜면 된다.
EOF는 End Of File의 약자로, Ctrl + Z(맥에서는 Ctrl + D)를 누르면 엔터를 누를 필요 없이 저절로 EOF가 입력된다.
이번에는 백준 예시 없이 간단하게 EOF가 나올 때까지 무한 입력을 받는 코드를 작성해보겠다.
#include <iostream>
using namespace std;
string str;
int main()
{
while (!cin.eof()) // 입력
{
cin >> str;
cout << str << '\n';
}
return 0;
}
위 코드를 직접 실행해보면 문자열을 입력할 때마다 그대로 출력된다.
그리고 OS에 따라 Ctrl + Z or Ctrl + D를 누르면 EOF가 입력되어 그것을 감지하고 while을 탈출하면서 프로그램이 종료된다.
가끔 대량의 입력을 받을 때, 입출력 속도가 느려서 시간 초과가 발생하는 경우가 있다.
그럴 때는 main 함수 최상단에 아래 코드를 끼워넣으면 된다.
ios_base::sync_with_stdio(false);
cin.tie(NULL);
cout.tie(NULL);
단, 이렇게 사용할 경우 scanf, printf 등을 사용하면 안된다.
cin, cout만 사용하면 빠른 입출력이 가능하다.
그리고 줄바꿈을 std::endl로 쓸 수도 있는데, '\n'이 더 빠르기 때문에 가급적 '\n'을 직접 쓰자.