https://www.acmicpc.net/problem/2675

#include <iostream>
#include <string>
using namespace std;
int main(){
ios::sync_with_stdio(false);
cin.tie(NULL);
int test_case;
cin >> test_case;
cin.ignore();
int cnt = 0;
while(cnt < test_case){
string s;
getline(cin, s);
for(int i = 2; i < s.length(); i++){
int mult = s[0] - '0';
for(int j = 0; j < mult; j++){
cout << s[i];
}
}
cout << '\n';
cnt ++;
}
return 0;
}
문제 자체는 크게 어렵지 않았다. 그래도 새롭게 알게 된 사실을 몇가지 적자면,
이 코드는 C와 C++ 표준 stream의 동기화를 비활성화 하라는 뜻이다.
동기화가 활성화 되어있을 땐 C의 printf, scanf 또는, C++의 cout, cin을 혼합하여 사용해도 문제가 없다.
하지만, 이 코드를 작성하면 C의 printf, scanf와 C++의 cout, cin을 혼합하여 사용할 수 없는 대신에,
C++의 cout, cin만 사용하면 기존 동기화 과정에서 필요하던 시간이 절약되어 입출력 속도가 빨라지는 효과가 있다.
따라서, 알고리즘 문제를 풀 때는 표준 stream의 동기화는 필요없고 시간이 절약되는 게 우선이니
ios::sync_with_stdio(false)를 사용하여 입출력 시간을 절약할 수 있다.
그러나,
- C의 표준 입출력 사용 시 오류 발생(printf, scanf, getchar, puts, gets)
- 멀티쓰레드 불가. 싱글쓰레드 환경에서만 사용(실무에서 사용 불가)
위와 같은 문제점 또한 존재한다.. 생각보다 파워풀한 코드이다. 활용 방법을 현명히 알고 사용하도록 하자.
C++ 에서는, cin을 통해 입력이 발생하면 cout 출력 버퍼(buffer)를 플러시(flush)시켜 출력이된 후 입력을 받는다.
버퍼(buffer)는 꽉 차면 출력이 되는 방식이며, 플러시(flush)는 버퍼가 꽉 차지 않아도 즉시 출력해서 버퍼를 비워주는 것이다.
cin과 cout은 서로 연결되어있어, cin을 쓰면 출력 버퍼를 비우고 입력이 발생한다. 당연히 이러한 flush 과정(버퍼를 비우는 작업)도 프로그램 속도에 영향을 끼친다.
따라서, cin, cout의 상호 연결을 끊어주기 위해 cin.tie(NULL)를 사용한다.
예를 들어, 아래와 같은 코드가 있다면,
cout << "Write your name, please \n" <<; cin >> name;기존에는 "Write your name, please \n" 가 반드시 먼저 출력된 후 이름을 입력할 수 있다.
그러나 cin.tie(0) 또는 cin.tie(null) 코드를 추가해 주면,
"Write your name, please \n" 출력이 되기 전에 이름을 입력할 수 있다.
내부적으로 cin과 cout을 묶어주는 과정을 수행하지 않기 때문에 시간이 절약된다.
컴퓨터공학과 전공이기 때문에, cin.tie(0) 또는 cin.tie(null) 둘 다 사용할 수 있는 이유에 대해 찾아보았다.
일단, cin.tie(0)와 cin.tie(nullptr)는 같은 의미이다. cin.tie() 함수는 ostream* 포인터를 인수로 받는다.
nullptr은 포인터가 아무것도 가리키지 않음을 나타내는 특수한 포인터 값이므로,
cin.tie(nullptr)을 cin과 cout을 분리하는데 사용할 수 있는 것이다. cin.tie(0)도 동일한 효과를 가지며,
많은 컴파일러에서 둘 다 동작할 것이다.
주의할 점은 cin.tie(nullptr) 또는 cin.tie(0)을 설정하면 cin과 cout이 더 이상 동기화되지 않으며, 버퍼링이 비활성화된다. 출력 버퍼를 직접 관리해야 하므로 주의해서 사용해야한다.
무슨 말이냐면, 아래와 같이 코드가 있을 때,
cin.tie(0)
cout << "Write your name, please \n" <<;
cin >> name;
다른 프로그램에서 이렇게 사용한다면 이름을 입력해달라고 요구하기 전에 입력을 할 수 있는 자연스럽지 못한 프로그래밍일 수 있다. 하지만, 알고리즘 문제를 풀 때는 크게 상관이 없고, 입출력 시간을 절약할 수 있기 때문에 cin.tie(0) 코드를 많이 사용한다.
cin은 문자를 입력받는 경우(여기서는 getline) 바로 변수에 저장되는게 아닌 문자가 입력버퍼에 저장되고 버퍼에 저장된 값을 읽어들여 변수에 저장한다.
여기서 버퍼에 대해 간단하게 설명을 하자면,
버퍼는 데이터를 한 곳에서 다른 곳으로 전달하는 동안 즉, 입력에서 변수로 저장되는 과정에서 일시적으로 데이터를 저장하는 메모리의 영역이다.
우리가 대부분 일상적으로 사용하는 버퍼링은 이런 버퍼를 활용하거나 버퍼를 채우는 방식을 의미한다.
그래서 cin에서 문자를 입력 받을 때는 입력버퍼에 있는 값을 읽어들여 변수에 저장을 하게 되는데
cin을 통해 숫자를 입력받는 경우는 그렇지 않다. 숫자는 버퍼를 거치지 않고 바로 변수에 저장된다.
그럼 이 문제처럼 숫자를 입력받고 문자를 입력받는 cin의 경우를 보면,
int test_case;
cin >> test_case;
cin.ignore();
int cnt = 0;
while(cnt < test_case){
string s;
getline(cin, s);
...
test_case를 입력받은 후에 getline은 입력받지 않고 바로 다음 코드인 stack을 생성하게된다.
그 이유는 버퍼에 정수값을 입력한 뒤 누른 '엔터'가 그대로 남아있기 때문에 이 엔터가 getline에 들어가기 때문이다.
이걸 사용하게 되면 입력 버퍼의 모든 내용이 제거가 되는데, 숫자를 입력 후 엔터를 치고나면 cin.ignore()에 의해서 엔터가 버퍼에서 제거되고, getline을 잘 수행을 한다.
간단한 문제였지만, 코드에 담긴 의미는 전혀 간단하지 않았다. 그래도, 개념을 하나하나 찾아가는 것이 꽤 재미있었다.
그리고 백준을 풀면서 다양한 사람들의 솔루션을 보는데, 대부분 입력을 한번에 읽어들어오는 것 같지 않는데, 어떻게 한번에 모든 case의 결과가 출력 되는지 의문점이 있었다.
왜 그런지, 어떻게 해결해야하는지 알게 되어서 하나의 궁금증이 또 풀려서 기쁘다.
이 내용은 또 따로 포스팅하여 올려야겠다 ㅎㅎ..