cin, cout 썼다가 런타임 오류가 나셨나요?
참고하세요~
- 알고리즘 [접근 방법]
방법 1 : C 표준 입출력 stdio.h
C언어의 표준 입출력인 scanf()와 printf() 쓰기
빠름의 정점인 C언어라 런타임 오류 안남
정답 코드
include <stdio.h>
int main(int argc, char const *argv[]) {
int T, a, b;
scanf("%d", &T);
for(int i = 0; i < T; i++){
scanf("%d %d", &a, &b);
printf("%d\n", a + b);
}
return 0;
}
C++인데 C언어 문법 쓰기 싫다면?
방법 2 : iostream의 default 설정 수정
cin, cout만 쓸 경우에는 시간초과가 남. 즉, 위 방법에 비해 상대적으로 느린 입출력이란 것.
왜?
1) C++와 C 표준 스트림의 동기화 해제
ios_base::sync_with_stdio(false);
기본적으로 C++에서는 C++와 C의 표준 스트림이 동기화가 됨. 무슨 말인가 하면, C++에서 C와 C++ 각각의 스타일로 입출력을 받아도 서로 동기화하여 우리가 입력 혹은 출력하고자 하는 순서대로 결과를 얻을 수 있음. 즉, C와 C++가 동일한 버퍼를 공유한다는 것
이러한 동기화는 성능을 저하시키는 원인이 되지만, 두 스트림의 동기화는 우리가 입출력에 있어 C와 C++의 IO(Input-Output)을 혼용하여 쓸 때 매우 합리적이고 스레드로부터 안전하기 때문에 원래는 동기화 상태로 두는 것이 올바름
(std::cin은 stdin과 동기화 되며, std::cout은 stdout과 동기화 됨)
다만, 알고리즘 문제 풀이에서는 예외 처리나 멀티스레드 작업을 필요로 하지 않기 때문에 두 동기화를 끊어주어도 무방함
이 동기화를 끊는 다는 것은 C++ 표준 스트림이 독립적으로 IO 버퍼링을 할 수 있다는 것. 그렇게 되면 상당히 많은 양의 입출력이 있을 경우 동기화 되어있는 상태에 비해 성능이 많이 좋아짐
즉, ios_base에 있는 sync_with_stdio() 을 활용하여 위 코드처럼 적용시키면 동기화가 해제 됨. 함수를 해석해보면, stdio와의 싱크(동기화) 메소드인 것을 알 수 있음. 여기에 파라미터로 false을 해주면 동기화가 해제되게 된다.
그리고 중요한 점은 동기화를 해제했기 때문에 C와 C++ 스타일 중 하나를 선택해서 써야 함. 혼용X
2) 입력과 출력 연결을 끊어주기
cin.tie(NULL); // 또는 cin.tie(nullptr), cin.tie(0) 으로 대체 가능
기본적으로 입력과 출력은 연결되어 있음
무슨 말인가 하면, 기본적으로 입력 요청이 들어오면 그 전에 출력 작업이 있었을 경우(출력 버퍼에 내용이 있는 경우) 버퍼를 비워(flush) 출력
좀 더 쉽게 말하자면 입력 요청을 통해 읽어들이게 될 경우, 전에 있던 출력 작업들을 콘솔창에 보이도록 버퍼를 비운다는 것
예를 들어
[입출력이 묶여있는 경우]
#include <iostream>
using namespace std;
int main(int argc, char const *argv[]) {
ios_base::sync_with_stdio(false);
int a;
for (int i = 0; i < 10; i++) {
cout << i << "번 째 입력\n";
cin >> a;
}
return 0;
}
위와같이 하면 콘솔 창에서는 다음과 같이 결과를 얻을 수 있을 것이다.
0번 째 입력
2
1번 째 입력
4
2번 째 입력
25
3번 째 입력
54
4번 째 입력
43
5번 째 입력
23
6번 째 입력
43
7번 째 입력
6336
8번 째 입력
4352
9번 째 입력
24
하지만, 입력과 출력의 묶음을 풀어주면?
[입출력이 분리되어있는 경우]
#include <iostream>
using namespace std;
int main(int argc, char const *argv[]) {
ios_base::sync_with_stdio(false);
cin.tie(NULL);
int a;
for (int i = 0; i < 10; i++) {
cout << i << "번 째 입력\n";
cin >> a;
}
return 0;
}
아래와 같이 출력됨
32
24
14
52
25
24
23
1
34
342
0번째 입력
1번째 입력
2번째 입력
3번째 입력
4번째 입력
5번째 입력
6번째 입력
7번째 입력
8번째 입력
9번째 입력
-- 엥? 아닌데요...?
아마 대부분의 경우 두 번째 코드를 실행해보아도 위 결과처럼 안나오고 첫 번째 결과처럼 나옴
이유는 OS마다의 버퍼링 차이가 있는데, 윈도우의 경우에는 기본적으로 출력 하자마자 버퍼링 없이 콘솔에 출력이 됨. 하지만, 리눅스 계열의 경우 줄(개행)을 입력 받을 때 까지 버퍼링
그래서 실제로 리눅스에서 실행시키면 아래처럼 나옴.
중요한 건, 백준 채점 서버는 우분투(리눅스 계열)라는 것. (참고링크 : www.acmicpc.net/help/judge)
그리고 백준에서는 입력과 출력을 별도로 분리하고 있어 "출력문"만 채점 파일과 동일하면 되기 때문에 굳이 매번 출력 할 필요가 없음.
즉, 출력문만 동일하면 되기 때문에 굳이 입력 후에 해당 값을 매 번 출력 해줄 필요가 없다. 즉, 매번 출력을 flushing 시키지 않고 나중에 한 번에 비우도록 하는 것!
3) endl 대신 "\n" 쓰기
endl은 단순히 개행(줄바꿈)만 해주는 것이 아니라 출력 버퍼를 비우는 역할. 즉, 매 줄 바꿈마다 endl 을 쓰면 우리가 2번에서 다루었던 tie을 끊어주는 것의 효과를 볼 수가 없음.
이 출력 버퍼를 비우는 작업도 상당히 시간을 잡아먹는 작업이기 때문에 마지막 한 번에 출력을 비우는 것이 좋음(물론 버퍼가 꽉 차면 알아서 비워줌.)
즉
ios_base::sync_with_stdio(false); // C, C++ 동기화 해제
cin.tie(NULL); // 입력과 출력을 분리
for(int i = 0; i < T; i++) {
int a, b;
cin >> a >> b;
cout << a + b << "\n"; // endl 대신 \n 을 쓰기
}
그 외에 입출력을 직접 구현하는 방법도 있지만, 이 포스터에선 다루지 않음
정답 코드
#include <iostream>
using namespace std;
int main(int argc, char const *argv[]) {
ios_base::sync_with_stdio(false);
cin.tie(NULL);
int T, a, b;
cin >> T;
for(int i = 0; i < T; i++) {
cin >> a >> b;
cout << a + b << "\n";
}
return 0;
}
'문제 풀이' 카테고리의 다른 글
백준 9325번 (0) | 2024.09.20 |
---|---|
111. 무인도 여행 (0) | 2024.08.09 |
109. 연속된 부분 수열의 합 (0) | 2024.08.07 |
108. 삼각 달팽이 (0) | 2024.08.06 |
107. 큰 수 만들기 (0) | 2024.08.05 |