written by yechoi

[C++] std::endl이 \n과 다른 점 - 출력버퍼 이해하기 본문

Born 2 Code/C, C++

[C++] std::endl이 \n과 다른 점 - 출력버퍼 이해하기

yechoi 2020. 11. 4. 20:41
반응형

C++ 시작하는 친구가 문득 std::endl이랑 '\n'이랑 뭐가 다르냐고 물었다. 이전에 훑어보기론 std::endl은 출력버퍼를 비우고 \n은 그렇지 않다고 봐, 이렇게 설명을 해줬다. 그랬더니 버퍼가 뭐냐, 버퍼를 비운다는 게 뭐냐, 출력버퍼를 왜 비우냐 묻는다. 정확히 이유를 알진 않고 관습적으로 사용하던 중이었는데, 막상 질문을 받으니까 너무나 궁금해지는 것,,, 이 참에 찾아보는 std::endl\n의 다른 점! 버퍼란 무엇이냐는 물음부터 시작한다.

출처: TCP 스쿨

 

 

버퍼란

버퍼는 임시 메모리 공간이다. 입력이나 출력을 바로바로 전달하는 게 아니라, 버퍼에 담아둘 수 있다. 버퍼를 사용하면서 얻을 수 있는 장점은

  1. 문자를 한번에 전달하므로 전송시간이 적게 걸림
  2. 문자를 잘못 입력했으면 수정 가능
  3. 시스템콜을 적게 호출해 자원을 아낌

이렇게 임시적으로 버퍼에 저장해두는 걸 버퍼링이라고 칭한다. 버퍼링의 방식엔 두 가지가 있는데

  1. 버퍼가 다 찰 때까지
  2. 개행을 의미하는 문자가 나타날 때까지

🔗콘솔 입출력
🔗Stdout Buffering

 

 

버퍼를 비운다는 건

버퍼에 저장돼있던 내용을 내보낸다는 거다. stdout 버퍼를 비우면, 화면으로 그 내용이 출력된다. 이를 flush 한다고한다.

버퍼를 비우는 케이스는 버퍼링을 하는 경우를 뒤집으면 된다.

  1. 버퍼가 다 찼을 때
  2. 개행을 의미하는 문자가 나타났을 때

앞선 2번의 예를 들자면

int        main(void)
{
    std::cout << "hello" << std::endl;
    sleep(5);
    return (0);
}

이 코드는 hello가 출력된 뒤 5초가 흐르고 종료된다.

int        main(void)
{
    std::cout << "hello";
    sleep(5);
    return (0);
}

이 코드는 5초가 흐르고 hello가 출력된다.

앞선 코드는 버퍼를 비우라는 사인인 std::endl이 나왔기 때문에, hello를 먼저 출력하고 종료한다. 뒤의 코드는 버퍼를 비우라는 사인이 없었기 때문에 buffer에 hello가 쌓인 채로 5초가 지나고, 프로그램이 종료되면서 커널에 의해 버퍼를 비운다.

한가지가 더 있는데 인위적으으로 비워주는 경우다.

  1. C 표준 라이브러리 fflush() 를 사용할 때
int        main(void)
{
    std::cout << "hello";
    fflush(stdout);
    sleep(5);
    return (0);
}

std::endl 이 있던 것처럼 먼저 hello를 출력하고 5초가 지난다.

🔗flush에 대한 고찰
🔗 C에서 입력버퍼(stdin)과 출력버퍼(stdout)

 

 

std:: endl과 \n의 차이

std::endl\n의 차이는 여기서 나타난다. std::endl은 위의 2번에 해당하는 문자다. std::endl이 입력되면 버퍼는 자동으로 비워진다.

\n의 경우 버퍼를 비우지 않는다. 다만 구현체에 따라서 std::endl처럼 버퍼를 비우도록 처리하도록 하는 경우도 있다. 그렇지만 장담할 수 없으므로, 버퍼를 비우고 싶으면 std::endl 을 사용해야 한다.

속도도 차이난다. 버퍼를 비우는 std::endl 이 느리고, 비우지 않는 \n 이 빠르다. 굳이 즉시 출력해 줘야하는 게 아니라면, \n으로 모아뒀다가 출력하는 게 시간을 줄일 수 있다. 백준에서 타임아웃 문제로 이 둘의 차이를 궁금해 하는 사람들이 많다.

~이 둘이 비우는 버퍼가 std::endl은 buffered IO, \n은 streamed IO로 다르다고 하는데, 구체적인 자료는 찾지 못했다. ~

🔗개행(\n)과 endl의 차이가 궁금합니다.

 

 

버퍼를 비워주지 않아서 문제가 생기기는 해?

개인적으로는 입력버퍼를 비우지 않아서 생기는 문제는 봤어도, 출력버퍼를 비우지 않아서 생기는 문제는 경험한 적이 없다. 그래서 처음에는 '굳이 비우는 걸 신경써야 하는 거야'라는 의문이 생겼던 것도 사실.

여러가지 경우가 있겠지만 이런 경우에 문제가 생길 수 있다.

  • 앞선 sleep() 사용 예제처럼 출력에 시간차가 나는 경우

  • fork() 로 프로세스가 나뉜 상황

여기서는 write와 printf를 혼용했을 때 문제가 생겼다고. write는 버퍼 내용을 그대로 출력하는 반면, printf는 종료문자('\0', '\n')를 만나기 전까지 출력하지 않기 때문.

갑자기 궁금증 폭발해서 찾아본 내용. 묵혀놨던 궁금증인 'C++에선 string1 == "abc" 이런 연산이 왜 먹힐까'가 다시 떠오르긴 했는데, 다시 묵혀두기로 하고...ㅋㅋㅋㅋ 코테 준비하러 총총...

반응형