Accelerated C++_Chapter01

gyeon·2021년 12월 19일
0

Accelerated C++

목록 보기
2/5

문자열 사용

밑줄친 내용들

  • 객체는 타입이 정해진 컴퓨터 메모리의 일부이다. -> 인스턴스?
  • 변수는 이름이 있는 객체이다.
  • 컴퓨터는 }에 도달하면 변수name은 소멸하고, 그 메모리를 반환한다. -> {}단위로 지역변수가 할당됨.
  • string의 + 연산은, string에 한정해서 + 연산자를 오버로딩(overload)한 것이다.
    또한, str1 + str2 연산시 str1, str2의 복사본을 결합한다.
  • 입력시 std::cin >> 변수를 사용하는데, istream타입의 std::cin을 반환한다.
    -> std::cin >> 변수std::cin과 변수를 overload된 >>연산을 하는 것이다. 그러므로 반환값이 있을수 있는게 놀라운건 아니다.
    std::cin >> 변수1 >> 변수2는 왼쪽부터 연산하므로 우선 std::cin >> 변수1를 연산하고, 이후 반환된 std::cin를 이용해 std::cin >> 변수2가 연산되는 것이다.

연습문제

1-1 const로 정의되었다고 하더라도, +연산시 복사본을 만들어서 하기 때문에, 에러가 발생하지 않는다.
1-2 1과 같지만, const로 정의된 변수를 나중에 연산해도 되는지에 대한 문제 같다. 결과는 상관 없다.
1-3 {}를 통한 지역변수의 생성과 소멸에 관한 문제 같음. 올바르게 동작하고,
결과는

a string
anotherstring

이 될거같음.
1-4 {}내부에 외부와 이름이 같은 지역변수가 선언되었을때, 이를 호출하면 나중에 선언된것이 호출되는것을 물어보는 문제 같다. 근데 내부의 {...}{...};로 바꿔서 해보라는 내용이 있는데... 무슨 의도인지 모르겠다.결과는 당연히 아래처럼 나온다

a string
anotherstring

1-5 {}내부에서 선언되면 해당 블럭을 나가면 반환된다. 따라서 x를 출력하는 부분에서 컴파일 에러가 발생한다. -> error: use of undeclared identifier 'x'
1-6 이부분은 버퍼 동작방식에 대해 더 공부하고 정리할 것.

스트림

c-annotationskr
입출력 장치로부터 들어오고 나가는 데이터를 추상화된 흐름 -> 데이터의 파이프라고 생각.
c에서도 표준입출력을 이용할수 있지만, c++에서는 스트림을 이용하여 class에 기반한 입출력 라이브러리를 제공한다. 이러한 스트림은 ~헤더를 사용한다. 스트림에서는 탬플릿을 사용하므로 printf나 scanf처럼 입/출력데이터의 형을 정할 필요가 없어진다. 또한 스트림 또한 객체이므로, 필요시 함수나 연산자를 오버로드(overload)할 수 있어서 확장성이 넓어진다.
단위는 byte이다.

스트림과 버퍼


이미지 출처 : [모두의 코드 C++](이미지 출처 : https://modoocode.com/213)
c++의 스트림 역시 입출력시 버퍼를 사용한다. 한 문자마다 입출력장치에 접근(시스템콜)하는것은 많은 비용을 필요로 한다. 하지만 입출력 장치가 데이터를 버퍼에 쌓아두고, 프로그램이 버퍼를 읽고 가져가기만 하면 되서 성능 측면에 더 유리하다.

flush과 cout

std::cin/out은 각 시스템에 맞는 입출력과 연결되게 해준다(시스템콜). 또한 cout은 << 연산자를 통해 입력을 안전하게 ostream객체(출력 버퍼 포함..?)에 전달한다. 이후 해당 버퍼가 전부 비워지는 fulsh가 되어야, 콘솔에 출력이 된다. fulsh의 조건은 다음과 같다.
flush는 버퍼를 모두 내보내는 역할을 한다.
flush조건 : 출력 버퍼가 꽉 찼을때, endl / flush조작자가 삽입될때

flush과 '\n'

원래는 '\n'으로는 flush가 되지 않는다. 하지만, 대다수의 구현체에서 '\n'시 flush한다고 한다.

'\n'은 표준에 정의된 대로는 출력 버퍼를 비우지 않습니다. 다만, 많은 구현체들이 '\n’을 출력할 때 출력 버퍼를 비우도록 하기는 합니다.
https://en.cppreference.com/w/cpp/io/manip/endl 8
In many implementations, standard output is line-buffered, and writing ‘\n’ causes a flush anyway, unless std::ios::sync_with_stdio(false) was executed. In those situations, unnecessary endl only degrades the performance of file output, not standard output.
…많은 구현체들에서, 표준 출력은 줄 단위로 버퍼되므로, '\n’을 쓰는 것은 어쨌든 버퍼를 비우기는 한다. std::ios::sync_with_stdio(false)가 실행되지 않는 한에서. 이러한 경우에, std::endl은 단지 파일 출력 (표준 출력 스트림이 아닌)의 성능을 저하시킬 뿐이다.
그러므로 std::endl = 오버헤드 있는 개행문자라고 보시면 되겠습니다.

C++ 언어 구현체들끼리 많이 다른 부분이기도 한데, 얼음나무님 말씀처럼 flush 를 지원하는 ‘\n’ 인 구현체들도 많습니다. buffered IO 와 streamed IO 도 구별되어야 하는데, ‘\n’ 이 flush 하는것은 C-Lib 의 buffered IO 고, std::endl 은 output stream 에 대한 flush 입니다. 이것 역시 구현체마다 달라질 수 있지만 어쨌든, 둘 은 서로 다른 flush 입니다. ( stream 쪽이 일반적으로 좀 더 무겁습니다 )
‘\n’ 도 운영체제 마다 컴파일러 마다, 연결된 IO 가 binary 인지 아닌지에 따라 다양하게 해석되기도 합니다. 0x0D 0x0A ( MS-DOS 호환 ) 로 해석되기도 하고 0x0A ( Unix 호환 ) 로 해석되기도 하니까요. 엄밀히는 CR + LF 는 0x0D 0x0A 여야 하는 것이지만 편의를 위해 ‘\n’ 을 확장 번역하기도 합니다. 또한 여러 언어코드를 지원할 경우 다른 동작이 구현되어 있을수도 있습니다.
어쨌든 정말 속도가 필요한 부분에서 cout 을 쓰는것 자체가 넌센스입니다. fast io 를 키워드로 검색해 참고하시기 바랍니다.

😵😵😵😵😵😵😵😵😵😵😵😵😵😵😵😵😵😵😵😵😵😵😵😵😵😵😵😵😵😵😵😵😵😵😵😵😵😵😵😵😵😵😵😵😵

하루종일 찾아봤는데 서로 말이 다 다르다. 믿을만한 한권의 책 내용을 정리하는게 더 좋을듯하다.

😵😵😵😵😵😵😵😵😵😵😵😵😵😵😵😵😵😵😵😵😵😵😵😵😵😵😵😵😵😵😵😵😵😵😵😵😵😵😵😵😵😵😵😵😵

버퍼링

cout << 을 통해 출력 버퍼로 데이터가 입력되었지만, flush되지 않아서 출력되지 않는 것.

#include <iostream>
#include <string>
#include <unistd.h>

int main() {
1	std::cout << "what is your name? : ";
2	std::string name;
3	sleep(5);
4	std::cin >> name;
5	std::cout << "Hello, " << name << " 1" << std::endl;
6	std::cin >> name;
7	std::cout << "Hello, " << name << " 2" << std::endl;
8
9	return 0;
}

위 코드의 동작과정
1. 위 코드를 컴파일해서 실행시키면 5초간 아무것도 뜨지 않는다.
2. 5초간 멈춰있는 상태에서, "gyeon hi"를 입력한다.
3. sleep이 끝나고 다음과 같이 출력된다.

what is your name? : Hello, gyeon 1
Hello, hi 2

해석

"what is your name? : "이 출력되지 않은 이유

std::cout을 통해 "what is your name? : "이라는 문자열이 버퍼로 들어갔지만, 버퍼가 비워지지 않아서(=flush되지 않아서) 버퍼안에 문자열만 들어가 있고, 콘솔에 출력되지 않는다. 출력시키러면 std::coutstd::endl을 입력?하면 된다(std::cout << "what is your name? : " << std::endl;).

std::cin >> name;호출 이전에 입력한 문자열이 입력으로 잘 들어간 이유

std::cin이 호출되어야 표준 입력을 할수 있게 하는게 아니라, 입력 버퍼의 데이터를 읽어오는 역할을 수행한다.

일단, 4번의 std::cin코드가 실행되기 전이라고 하더라도, "gyeon hi"라는 데이터가 표준 입력을 통해 입력 버퍼안에 들어와 있는 상태이다. ->검증 필요

이후 4번줄에서 std::cin이 호출되면 입력버퍼에서 Whitespace가 나올때까지 읽고 이를 name으로 넘긴다. 입력버퍼의 스트림 위치 지정자는 끊긴 위치로 이동한다.

버퍼에만 있던 "what is your name?"이 출력된 계기

std::cout에 새로운 데이터인 "Hello, ..."가 들어오게 되므로, 출력버퍼가 비워지며 "what is your name?"이 출력되게 되는거 같은데... 왜?
이후 출력버퍼에 차례로 "Hello, ", name의 "gyeon" 그리고 " 1"이 들어온다. 이후 마지막으로 std::coutstd::endl이 전달되어 flush되어 버퍼의 내용들이 한번에 출력된다.

"Hello, hi 2"가 출력된 이유

입력버퍼의 스트림 위치 지정자는 " hi"라는 내용을 가르키고 있다. 따라서 std::in을 통해 다시 입력 버퍼의 내용을 읽어오게 되면, "hi"부터 이어서 읽어오게 된다.

참고한 곳들...
https://docs.microsoft.com/ko-kr/cpp/standard-library/effects-of-buffering?view=msvc-170
https://yechoi.tistory.com/48
https://codingdog.tistory.com/entry/%ED%95%AD%EC%83%81-flush%EB%A5%BC-%ED%95%B4%EC%84%9C-%EB%8A%90%EB%A6%B0-c-endl
https://c-annotationskr.sourceforge.io/cplusplus06.html
https://www.codentalks.com/t/topic/8869

profile
백엔드와 서버 in 42Seoul

0개의 댓글