[C++] 표준 입력 scanf() 함수와 객체 std::cin

scanf()와 std::cin 비교

scanf() 함수와 std::cin 객체 사용 모두 사용자의 입력을 받아들이는 방법입니다.

그런데 제목을 자세히 보신 분은 눈치챘겠지만 scanf()는 함수로 C부터 널리 사용되어 왔습니다.

int scanf( const char *format [,argument]...);

 

 

이에 반해 std::cin은 객체 지향 언어인 C++의 객체로 좀 더 자세히 말하면 전역스트림 객체입니다.

정의를 보자면 아래와 같습니다.

extern istream cin;

 

 

std::cin 객체를 사용하기 위해서 <iostream> 헤더파일을 포함해야 하고, scanf() 함수를 사용하기 위해선 <stdio.h>나 <cstdio> 헤더파일을 포함해야 하지만 <iostream> 헤더파일에도 정의되어 있기 때문에 굳이 구분하여 사용할 필요는 없을 것 같습니다.

#include <iostream>

 

두 방법은 구현 방식과 방법론이 다르기 때문에 사용하는 방법 또한 다릅니다. 

사용법을 비교해서 정리해 보도록 하겠습니다.

 

scanf 사용법

scanf 함수를 사용할 때는  입력받고자 하는 데이터 타입에 따라 타입 지정자를 설정해야 합니다.

예를 들어, 정수를 입력받으려면 타입 지정자 "d"를 사용합니다.

int A = 0;
scanf("%d", &A);	// 변수 A의 주소를 넣어야 한다

사용자가 10을 입력하면 A값이 10으로 변경됩니다.

 

여기서 주의해야 할 점은 값을 받아올 변수 A를 함수의 인자로 사용하면 안 되고, 변수 A의 주소를 인자로 넘겨주어야 한다는 겁니다. 함수 내의 변수는 함수 범위 내에서만 효력이 있고, 다른 함수로 넘어가면 효력이 없기 때문입니다.

void isFunction( int A ){
    A = 3;
}

void isFunction2( int b ){
    b = 3;
}

void isFunction3( int b ){
	
    // A가 isFunction3 내에서 선언하지 않은 변수로 효력이 없다
    // 컴파일 오류!!
    A = 3;	
}

int main(){
    int A = 0;
    isFunction(A);
    
    cout << A;	// A의 값 출력
    return 0;
}

이 코드를 보면 main 함수에서 변수 A를 0으로 초기화하고 isFunction 함수에 인자로 넘기고 있지만 isFunction 함수로 넘어가면 main 함수의 변수 A는 효력을 잃게 됩니다. 또한 isFunction의 A는 main 함수의 A와 문자만 같을 뿐 같은 변수가 아닙니다.

이해를 돕기 위해 isFunction2를 보면 isFunction과 완전히 똑같은 함수입니다. 단지 선언한 변수의 문자가 다를 뿐이죠.

 

따라서 isFunction 내에서 A를 3으로 변경하지만 isFunction에서 리턴하는 순간 isFunction안의 A는 사라집니다.

그리고 프로그램 제어가 main 함수로 다시 넘어오게 되면서 main 함수에서 선언한 A 변수가 다시 효력을 갖게 되는 것이죠. 그래서 A값이 변경되지 않습니다. 

 

그럼 어떻게 A의 값을 바꿀까요?

isFunction의 인자로 A의 메모리주소를 넘기는 겁니다. 집배원에게 주소를 알려주면 소포를 넣어주는 것과 같죠.

 

그래서 scanf 함수를 사용할 때 인자로 입력된 값을 받을 변수의 주소를 넘겨주게 되는 것입니다.

변수의 주소를 넘겨주기 위해서 주소 연산자 &를 사용합니다.

 

 

여러 종류의 입력을 얻기 위한 scanf 함수의 타입 지정자는 다음과 같습니다.

타입 지정자 입력 받으려는 데이터 형식
d 10진법으로 표현된 정수
e, E, f, g, G 소수점을 표함하는 소수
o 8진법으로 표현된 정수
s 문자열
u 부호가 없는 10진법으로 표현된 정수
x, X 16진법으로 표현된 정수
c 문자

 

scanf 함수를 이용해서 정수, 소수, 문자열을 입력받으려면 다음과 같을 것입니다.

int main() {	
    int a;
    float b;
    char str[128] = {'\0'};     // 초기화

    scanf("%d", &a);
    scanf("%f", &b);    
    scanf("%s", str);	// 배열명은 배열의 주소를 나타낸다. 주소 연산자가 필요없다.
    
    return 0;
}

 

게다가, 입력은 한 번에 여러 값을 받을 수도 있습니다. 이때, 입력값 사이의 공백은 무시됩니다.

int main() {	
    int a;
    float b;
    char str[128] = {'\0'};     // 초기화

    scanf("%d %f %s", &a, &b, str);
        
    return 0;
}

입력값으로 "10  3.5  Birthday"라고 입력하면 a에 10, b에 3.5, str에 Birthday라고 입력됩니다.

 

scanf 함수 정의를 보면 int값을 리턴한다고 했는데 정확히는 어떤 값을 되돌려줄까요?

int main() {	
    int a, b, c;
    
    // 입력 3 4 5 를 입력했을 때
    int nRet1 = scanf("%d %d %d", &a, &b, &c);	// 리턴: 3
    int nRet2 = scanf("%d %d", %a, %b, %c);	// 리턴: 2
    int nRet3 = scanf("%d %d %d", %a, %b);	// 오류!!
    
    int nRet4 = scanf("", &a, &b, &c);	// 리턴:0, 바로 함수 종료
    
    // 입력 1 ^z(EOF) 를 입력했을 때
    int nRet5 = scanf("%d %d %d", &a, &b, &c);	// 리턴:1
    
    // 입력 ^z(EOF)를 입력했을 때
    int nRet6 = scanf("%d %d %d", &a, &b, &c);	// 리턴:-1
    
    return 0;
}

scanf 함수는 성공적으로 읽은 입력값 개수를 리턴합니다. 그리고, EOF (파일 끝 - 윈도에선 ctrl+z) 신호를 만나면 그때까지 읽은 입력값 개수를 리턴하고 함수는 종료합니다. 하나의 데이터도 읽지 못하고 EOF를 만나면 -1 값을 리턴합니다.

(C++에서 EOF라는 키워드는 상수로 -1을 가집니다.)

 

이 리턴값을 이용하면 정해지지 않은 개수의 데이터를 처리하고 프로그램을 종료하는 코드를 작성할 수 있습니다.

예를 들어, 정해지지 않는 개수의 정수들 값을 입력받아 합을 구하는 코드를 작성해 보면 다음과 같습니다.

#include <iostream>

int main(){
    int sum = 0, a;
    
    while(true){
        int nRet = scanf("%d", &a);
        if (nRet == 0 || nRet == EOF){	// 읽은 것이 없거나 EOF를 만나면 종료
            break;
        }
        sum += a;
    }
          
    cout << "sum: " << sum;	// 총합 출력
    return 0;
}

 

 

이 함수는 타입 지정자 외에도 다양한 옵션이 있습니다. 더 복잡한 기능이 필요한 경우 참조할 수 있는 사이트를 링크합니다.

http://www.cplusplus.com/reference/cstdio/scanf/

 

std::cin 사용법

std::cin 객체로 입력받고자 하면 >> 연산자를 사용합니다.

정수를 입력받고자 하면 다음과 같습니다.

#include <iostream>
using namespace std;

int main(){
    int A;
    cin >> A;
    
    return 0;
}

 

참고로 using namespace std; 문장을 사용하게 되면 std라는 namespace를 전역으로 사용하겠다는 의미이기 때문에  std namespace 안에 구현되어 있는 cin 개체를 사용할 때  std::cin 대신 cin만 사용할 수 있습니다.

앞으로 std namespace를 전역으로 사용한다고 가정하고 간단히 cin으로 표기하겠습니다.

 

그럼 소수를 입력받으려면 어떻게 할까요?

간단하게도 소수형 변수를 선언하고 >> 연산자에 대입하면 됩니다. 정수를 입력받을 때와 같습니다.

float B;
string C;	// std::string 개체

cin >> B;	// 소수
cin >> C;	// 문자열

 

scanf() 함수를 사용할 때는 입력을 받고자 하는 변수의 타입을 고려하며 입력받아야 하지만, cin을 사용할 때는 그러한 번거로운 일이 없습니다. C++에서는 연산자들을 재정의 (operator overloading) 해서 사용할 수 있게 해주는 방법을 제공하기 때문입니다. cin 객체는 정수, 소수, 문자열 등의 다양한 타입의 입력을 처리하는 코드를 구현해 놓았고 입력된 변수에 따라 자동으로 적절한 처리를 수행합니다. 눈에 보이지 않을 뿐이죠.

 

또한, 여러 개의 값을  한 번에 입력받을 수도 있습니다.

 int A;
 float B;
 string C;
 
 cin >> A >> B >> C;

 

이러한 문장이 가능한 것은 >> 연산자의 리턴값이 다시 자기 자신의 cin 객체를 돌려주기 때문입니다.

istream & operator>>(int& n);	// cin은 istream 객체입니다.

 

 

scanf() 함수는 리턴값을 통해 EOF에 도달했음을 알 수 있었습니다. 하지만 cin의 >> 연산자는 객체를 돌려주기 때문에 EOF에 도달하는 것을 알기 위해선 eof() 멤버 함수를 호출해야 합니다. EOF에 도달하면 eof() 함수는 true를 돌려줍니다.

 

위에서 scanf()를 사용해서 정해지지 않는 개수의 정수들 값을 입력받아 합을 구하는 코드를 std::cin 객체를 사용해서 구현하면 아래와 같습니다.

#include <iostream>
using namespace std;

int main() {

    int sum = 0, a;

    while(true){
    	
        cin >> a;
        
        if (cin.fail() || cin.eof())  // 읽은 것이 없거나 EOF를 만나면 루프 탈출
            break;
    	sum += a;
    }

    cout << "sum: " << sum;     // 총합 출력
    return 0;
}

 

  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유