1 분 소요

오늘 공부하던 중 발견한 부분.

cin을 통해 값을 하나 입력 받은 후 getline으로 다른 문자열을 입력 받으려고 할 때 입력을 한번 건너뛰는 현상이 생긴다.

2026년 수정 안내 이 글에 처음 적었던 해결책은 fflush(stdin)이었는데, 이는 C/C++ 표준에서 정의되지 않은 동작(undefined behavior) 이다. 일부 환경에서 우연히 동작할 뿐이라 권장할 수 없다. 원인 설명과 해결책을 모두 바로잡는다.

왜 건너뛰는가

std::cin >> temp;는 공백 문자를 만나면 거기서 읽기를 멈추는데, 그 공백 문자(사용자가 누른 Enter, 즉 \n)를 스트림에서 꺼내지 않고 남겨둔다.

그 상태에서 std::getline을 호출하면, getline은 개행 문자를 만날 때까지 읽는 함수이므로 스트림 맨 앞에 남아 있는 \n을 즉시 만나 빈 문자열을 읽고 곧바로 반환한다. 입력을 건너뛴 것처럼 보이는 이유가 이것이다.

“다른 이벤트가 더 온다”는 식의 문제가 아니라, 단순히 버퍼에 개행 하나가 남아 있는 것이다.

왜 fflush(stdin)은 안 되는가

C 표준(그리고 C++ 표준)은 fflush출력 스트림에 대해서만 정의한다. 입력 스트림에 쓰면 정의되지 않은 동작이다.

  • MSVC는 확장으로 입력 버퍼를 비워주기 때문에 윈도우에서는 동작하는 것처럼 보인다.
  • glibc(리눅스)에서는 아무 일도 하지 않는다. 그래서 같은 코드가 리눅스에서 그대로 실패한다.
  • 무엇보다 stdin은 C 표준 입출력의 버퍼이고 std::cin은 C++ 스트림의 버퍼라, 대상 자체가 다르다. (sync_with_stdio가 켜져 있어 우연히 연결되어 있을 뿐이다.)

올바른 해결책

남아 있는 개행을 명시적으로 버리면 된다.

#include <iostream>
#include <limits>
#include <string>

int main()
{
    std::string temp;
    std::cin >> temp;

    // 현재 줄에 남아 있는 문자를 개행까지 전부 버린다.
    std::cin.ignore( std::numeric_limits<std::streamsize>::max(), '\n' );

    std::string msg;
    std::getline( std::cin, msg );

    std::cout << "temp: " << temp << "\nmsg: " << msg << std::endl;
    return 0;
}

ignore의 첫 번째 인자는 “최대 몇 글자까지 버릴지”이고, 두 번째 인자는 “여기까지 버린다”는 구분자다. std::numeric_limits<std::streamsize>::max()를 넘기면 개수 제한 없이 개행을 만날 때까지 버리라는 뜻이 된다. (<limits> 헤더가 필요하다.)

std::cin.ignore()처럼 인자 없이 부르면 한 글자만 버린다. 사용자가 값 뒤에 공백을 여러 개 입력한 경우를 놓치므로, 위 형태를 쓰는 것이 안전하다.

아예 섞어 쓰지 않는 방법

더 확실한 방법은 >>getline을 섞지 않고, 모든 입력을 getline으로 받은 뒤 파싱하는 것이다.

#include <sstream>

std::string line;
std::getline( std::cin, line );

int value = 0;
std::istringstream( line ) >> value;   // 줄 단위로 받아 파싱

이러면 스트림에 개행이 남는 상황 자체가 생기지 않는다.

참고자료

댓글 남기기