C++ 객체지향프로그래밍 수업

[C++] 4. stream(스트림)

인생 걸고 삽질 2022. 12. 1. 01:53

10주차 수요일 수업

이번 시간에는 stream iniput/output에 대해 배운다.


stream의 근본

stream(스트림)은 내 코드와 장치(키보드, 모니터, 파일, 네트워크...)를 연결해 주는 객체이다.

  • 예를 들어 내 코드에 cin을 썼다면 프로그램에서 입력을 받아 그 값을 내 코드에 집어 넣을 수 있다. 이 때 cin이 stream객체이다.

 

stream은 buffer(버퍼)를 거쳐서 장치와 연결된다.

  • 예를 들어 키보드에서 'a'입력이 들어오면, 바로 프로그램에 출력하는 게 아니라 buffer에 저장해 뒀다가 개행문자가 들어오거나 buffer가 꽉 찼을 때 한 번에 buffer를 싹 비운다.

 

input은 장치에서 메모리로, output은 메모리에서 장치로 byte가 흐르는 것이다.

  • input(cin): device->main memory
  • output(cout): main memory->device

 

input과 output 연산은 기본적으로 굉장히 느린 연산이다.

 

 


Unicode와 ASCII code

Unicode(유니코드)는 ASCII code(아스키 코드)보다 bit 수가 많은 대신 표현가능한 문자가 훨씬 많다.

  • ASCII 코드는 1byte(1bits)를 사용하기 때문에 표현 가능한 문자의 개수가 한정되어 있다. 다음과 같다.

아스키코드표

  • 이 때 8bit인데도 표현 가능한 문자가 2^7개인 128개인 이유는 한 개의 패리티 부호가 포함되어 있기 때문이다.
  • 이 때 패리티 부호는 논리회로 시간에서 배웠던 것처럼 오류가 있는지 없는지 표시하는 bit이다.
  • UNICODE는 비트의 수가 2~4byte인 대신 표현 가능한 문자가 훨씬 많다. 한글은 물론 고대영어까지 표현 가능하다고...

 


C-style string

char 배열을 이용하여 string처럼 쓸 수 있다.

  • 안전성이 떨어지는 대신 잘만 쓰면 string 객체보다 훨씬 효율적이다.
char cStyleLine[13];
cStyleLine="Hello World!"

 

 

char 배열은 크기를 지정해 주지 않아도 상관 없다.

  • 배열의 마지막에 NULL이 들어가고, NULL을 만나는 순간을 문자열의 끝으로 친다.
  • 예를 들어, 다음 코드의 출력은 eem이다.
	char arr[] = "eemune";
	arr[3] = NULL;
	cout << arr;

 

 

char*형 변수는 역참조 연산이 기본값이다.

  • 예를 들어 다음과 같은 코드로 int 배열을 만들었다면 배열의 이름은 첫 주소를 가리킨다. 따라서 역참조를 하려면 역참조 연산자 *를 따로 붙여 줘야 한다.
int arr[10]={1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
cout<<*(arr+2); //2번째 수 출력
  • 하지만 char형 배열은 포인터 변수가 기본적으로 역참조 연산으로 된다.
char arr[13]="Hello world!";
cout<<arr; //Hello world! 출력. *arr이 아니다!
  • 이 때 arr의 주소를 출력하고 싶다면 다음과 같이 하면 된다.
char arr[]="Hello World!"
cout<<static_cast<void*>(arr);

 

 

literal string의 data type은 const char*형이다.

  • 예를 들어, 다음과 같은 코드는 근본없다.
char* noRoot="eemune"; //error
  • 다음과 같이, 리터럴 문자열의 자료형은 const가 붙어야 한다.
const char* yesRoot="eemune";

 

 

문자열 내용이 완전히 같으면 다른 변수여도 메모리 주소는 같다.

  • 예를 들어, 다음과 같은 코드에서 line1의 주소와 line2의 주소는 같다.
char* line1="eemune";
char* line2="eemune";

 

 


istream class의 << operator

 

cin 객체는 buffer에 내용이 있으면 그걸 받아오고 아니면 입력을 새로 받는다.

  • 예를 들어 다음 코드에서, buffer에 다음과 같은 문자가 들어가 있다면 cin 객체가 가지고 있는 값은 ee가 된다.

buffer

>>, << operator는 white space를 모두 무시한다.

  • white space는 스페이스, 엔터, 탭 등 모든 공백을 의미한다.
  • 예를 들어 다음 코드를 보자.
	string line;

	cin >> line;  //1번 cin
	cout << line<<endl;

	cin >> line;  //2번 cin
	cout << line << endl;

	cin >> line;  //3번 cin
	cout << line << endl;
  • cin 받기 전: buffer는 비어있음
  • 1번 cin: buffer가 비어있기 때문에 사용자에게 입력받음. buffer에 있는 ee를 line에 저장. << operator가 ee직후의 공백은 buffer에서 버린다.

1번 cin에서 buffer 상황

  • 1번 cout: line에 저장되어 있는 ee를 출력한다. 이 때 buffer 상황은 다음과 같다.

1번 cout에서 buffer 상황

  • 2번 cin: buffer가 차있기 때문에 >>연산자가 buffer로부터 입력받는다. line에는 mu가 저장된다.

2번 cin할 때 buffer

  • 2번 cout: buffer랑은 관련이 없다. 이미 line 변수에 저장되어 있는 mu를 출력. 이 때 buffer 상황은 다음과 같다.

2번 cout할 때 buffer

  • 3번 cin: buffer가 차있기 때문에 >>연산자가 buffer로부터 line에 입력받는다. line에는 ne가 저장된다. 마찬가지로 개행문자(엔터)인 \n은 무시되고 buffer에서 비워진다. 이제 buffer에는 아무것도 없다.
  • 4번 cout: line 변수에 저장되어 있는 ne를 출력한다.

 

조건문 안에서 <<가 쓰이면 true/false를 return한다.

  • 제대로 cin이 됐으면 true, 아니면 false

 

 

 

 

istream class의 state bits

cin 객체에는 객체의 상태를 표현하기 위한 bit 3개가 있다.

  • cin 객체 안에는 bad bit, fail bit, eof bit가 있다. 기본 상태는 0이다.

cin 객체의 state bits

  • bad: 데이터가 손실되었을 시 1이 된다. (보통 복구가 불가능하다)
  • fail: data type이 다른 값이 들어왔을 시 1이 된다.
  • eof: 파일의 끝을 만났을 때 1이 된다. (^z같은...)
  • state bits가 000일 때만 cin을 받는다. 다음과 같이 000으로 초기화할 수 있다.
cin.clear();

 

 

state bits를 16진수로 return하는 rdstate()

  • istream의 멤버함수 중 rdstate()가 있다. 다음과 같이 쓴다.
cin.rdstate();

 

 

 

 

get()과 getline()

cin.get()은 buffer의 bit 한 개를 return한다.

  • white space를 무시하지 않는다!

 

cin.get(변수)는 buffer의 bit 한 개를 변수에 저장한다.

 

cin.get(arr, n, delimeter)는 buffer의 bit들 n개를 arr에 저장하되 delimeter 이전까지만 저장한다.

  • 예를 들어 다음과 같은 코드일 경우 arr에는 eem이 저장된다.
char[] line;
cin>>line;  //입력: eemune
char arr[];
get(arr, 6, u);

  • delimeter를 따로 지정해 주지 않으면 기본적으로 '\n'가 delimiter로 된다.

 

 

cin.getline()은 delimiter를 buffer에서 삭제한다.

 

 

 

 


노트 필기

 

cin 객체는 buffer에 내용이 있으면 그걸 받아오고 아니면 입력을 새로 받는다.