[BOJ/C++] cin.tie(NULL)과 ios_base::sync_with_stdio(false) 그리고 endl...

gogori6565·2022년 7월 17일
4

백준 문제 15552번의 가장 큰 핵심포인트!
cin.tie(NULL)ios_base::sync_with_stdio(false) 그리고 endl에 대해 알아보자

문제를 풀기에 앞서 문제 내용에 이런 문장이 있다.

"C++을 사용하고 있고 cin/cout을 사용하고자 한다면, cin.tie(NULL)과 sync_with_stdio(false)를 둘 다 적용해 주고, endl 대신 개행문자(\n)를 쓰자. 단, 이렇게 하면 더 이상 scanf/printf/puts/getchar/putchar 등 C의 입출력 방식을 사용하면 안 된다."

시간을 줄이기 위한 키포인트 같은데..
뭔지는 알고 적용해야 할 것 같아서 알아보았다.

👉 cin.tie(NULL)ios_base::sync_with_stdio(false) 은 대게 main의 첫번째 줄에 작성한다.


ios_base::sync_with_stdio(false)

💡 ios_base::sync_with_stdio(false) : C의 stdio와 C++의 iostream의 동기화를 비활성화 한다 (즉, 평소엔 서로 동기화 상태)

기본적으로 C++와 C의 표준 스트림은 동기화되어있다. 즉, C++에서 C와 C++ 각각의 스타일로 입출력을 받아도 서로 동기화해 우리가 입력/출력하고자 하는 순서대로 결과를 얻을 수 있다. C와 C++가 동일한 버퍼를 공유한다는 것이다.

  • 단, 이 경우 iostream과 stdio의 버퍼를 모두 사용하여 딜레이가 발생한다(성능저하)
    대신, 두 스트림이 동기화하여 입출력에 있어 C와 C++의 IO(Input/Output)을 혼용해 쓸 경우 합리적이고, 스레드(thread)로부터 안전하다.

ios_base::sync_with_stdio(false)을 통해 동기화를 끊으면 C++ 표준 스트림이 독립적으로 IO 버퍼링을 하게 되어 많은 양의 입출력이 있을 경우 성능이 많이 좋아진다.

  • 알고리즘 문제 풀이에서는 예외 처리나 멀티스레드 작업이 필요하지 않기에 두 동기화를 끊고 성능을 높여 시간을 줄이는 게 더 효율적이다.

🛑 주의사항!

  • 동기화가 비활성화된 경우,
    버퍼가 분리되었기 때문에 cin과 C의 scanf, gets, getchar 등을 같이 사용하면 안되고,
    cout와 C의 printf, puts, putchar 등과 같이 사용하면 안된다.

cin.tie(NULL)

💡 cin.tie(NULL) : 입력과 출력 연결을 끊어준다 (cin.tie(nullptr) 또는 cin.tie(0) 도 가능)

원래 coutcin, 입출력은 묶여있다.
입력요청이 들어오면 그 전에 출력 작업이 있었을 경우(출력 버퍼에 내용이 있는 경우) 버퍼를 비워(flush) 출력하게 된다.

  • 이 경우, 입출력이 반복될 때, 일일이 버퍼를 지우느라 시간이 오래 걸린다. 따라서, cin.tie(NULL) 을 통해 입출력 묶음을 풀면 시간이 단축된다.

🛑 주의사항!

입출력 순서를 보장받을 수 없다.

예를 들어 좀 더 자세히 설명해보겠다.

[입출력이 묶여있는 경우]

#include<iostream>
using namespace std;

int main(void){
	ios_base::sync_with_stdio(false);

    int t,num1,num2;
    cin>>t;

    for(int i=0;i<t;i++){
        cin>>num1>>num2;
        cout<<num1+num2<<"\n";
    }
}

이때, 입력과 출력은 순서대로 이루어진다.
입력 한 번에 출력이 매번 이루어지는 것이다.

2     //test case
1 1   //입력
2     //출력
2 2   //입력
4     //출력

[입출력 묶음이 풀려있는 경우]

#include<iostream>
using namespace std;

int main(void){
	cin.tie(NULL); //입출력 묶음 해제
    ios_base::sync_with_stdio(false);

    int t,num1,num2;
    cin>>t;

    for(int i=0;i<t;i++){
        cin>>num1>>num2;
        cout<<num1+num2<<"\n";
    }
}

이 경우, 입출력이 순서대로 출력되지 않는 걸 볼 수 있다.

2	  //test case
1 1   //입력
2 2   //입력
2     //출력
4	  //출력

입력을 한 번에 받고, 출력이 나중에 한 번에 처리된다.
즉, 매번 출력을 비우지(flush)않고 나중에 한 번에 비우도록 하는 것이다.


endl 대신 "\n"

endl 은 개행 뿐만아니라 출력 버퍼를 비우는(flush) 역할도 한다.

  • 이 출력 버퍼를 비우는 작업도 상당히 시간을 잡아먹기에 마지막 한 번에 출력을 비우는 것이 좋다. (물론, 버퍼가 꽉차면 알아서 비워준다)

+) endl은 출력 버퍼를 비우기 때문에 위에서 다룬 cin.tie(NULL)의 역할인 묶음을 끊어주는 효과를 볼 수 없게 한다.

["\n"대신 endl을 사용할 경우]

#include<iostream>
using namespace std;

int main(void){
    cin.tie(NULL); ios_base::sync_with_stdio(false);

    int t,num1,num2;
    cin>>t;

    for(int i=0;i<t;i++){
        cin>>num1>>num2;
        cout<<num1+num2<<endl;
    }
}

분명 cin.tie(NULL) 을 작성했음에도 endl 이 출력 버퍼를 비워 입력과 출력이 순서대로 이루어지는 결과를 볼 수 있다.

2     //test case
1 1   //입력
2     //출력
2 2   //입력
4     //출력

15552번 문제 제출하면서 cin.tie(NULL)ios_base::sync_with_stdio(false)를 뺀 경우와, endl 을 적용시킨 두 경우 모두 제출해보았는데 둘 다 시간초과 가 떴다!

문제 출처 : https://www.acmicpc.net/problem/15552

profile
p(´∇`)q

1개의 댓글

comment-user-thumbnail
2025년 3월 16일

정리 잘 해 주셔서 이해가 쉬웠습니다
다만 cin.tie(NULL) 을 적용하면 입력과 출력이 반복되지 않고 한번에 처리 된다고 하셨는데
동일하게 작성한 결과 입출력이 순서에 맞게 동작 되어 댓글 남기게 되었습니다

cpp 2022, x64, 릴리즈, 디버그 모드 전부 동일한 동작입니다

답글 달기

관련 채용 정보