Chapter 5. Loops and Relational Expressions

지환·2022년 6월 12일
0

for loops

  1. 초기값 세팅 (or 아무 expression)
  2. test (or 아무 expressin)
  3. loop body 실행
  4. value update (or 아무 expression)

(C++에서 2번의 expression의 결과를 bool로 변형해 판단한다. C나 예전 C++처럼 nonzero인지 0인지로 판별하는거나 bool로 변형해서 판별하나 그게 그거임, 즉 다를건 딱히 없다.)

for문 전체를 하나의 statement로 본다.
for문은 들어갈때 조건을 test하므로 entry-condition loop 라 한다.

함수와 구분하기위해
for, if, while 등은 옆의 괄호와 한칸 띄우고,
함수는 붙여쓰는 것이 common C++ style

Expressions & Statements

C와 마찬가지로 expression은 operators와 operands로 이루어지고,
expression에 `;` 추가하면 statement가 됨.
역은 성립하지 않는다.
즉, statement에서 `;` 뺀다고 다 expression이 되는건 아니다.
statement와 expression은 애초에 다른 범주이고, expression이 statement로 바뀔 수도 있단 것 뿐이다.

모든 expression은 value를 갖는다.
relational operator는 bool value를 갖는다.(0, 1 값 산출하는 C랑은 좀 다르네, 뭐 그게 그거다만은)

coutbool 값을 출력할때 int로 변환해서 0 또는 1을 출력한다.
cout.setf(ios_base::boolalpha); 라고 해두면, 그 다음부턴 booltype을 truefalse로 출력되도록 한다.

Bending the Rules
for (expression; expression; expression) statement
알다시피 이게 원래 syntax

여기서 첫번째 expression에 declaration이 올 수 있도록 허용한다. 근데 그렇게 해버리면 위 문법에 들어가지 않는다 왜냐하면 declaration은 expression이 아니니까.
그래서 declaration-statement expression 이라고 semicolon을 없앤, for statement에서만 쓸 수 있는 새로운 expression으로 정의함으로써 이를 해결했었다.

하지만 이런 방식도 취소되고 for statement의 문법을 다음과 같이 변경했다.
for (for-init-statement condition; expression) statement
for-init-statement는 expression statement이거나 declaration 이다.

for-init-statement가 expression statement이든 declaration이든
그 자체가 뒤에 semicolon을 포함하므로 위 문법엔 semicolon이 하나밖에 안보이는 것

이렇게 선언된 변수는 for문 벗어나면 eliminated

increment/decrement operator
C에서처럼 "일단 기존 값 사용 후 증가", "증가 후 증가된 값을 사용" 이정도는 정의돼있지만(ex. *(pt++);: pt원래 주소 dereference 이후 포인터 값 증가),
정확한 증감 지점이 '다음 sequence point 이전' 이란 것 말고는 모른다.
이 점이 모호하니 조심해서 사용하도록

i++, ++i 이렇게 단독으로 쓰이는건 prefix나 postfix나 차이가 없다. 효과도 그렇고 그게 그거다.
그런데 i가 built-in type이고, modern compiler라면 그렇지만,
C++은 class에 대해 이런 연산을 정의하도록 해준다.

그래서 class에 대해 이런 연산을 한다면, prefix는 그냥 증가시키고 반환하지만,
postfix는 값을 따로 저장하고 증가시키고 저장한 값을 반환해야해서
class(user defined type)에 대해서라면 prefix 연산이 낫다.

sequence point란?
해당 point 이전의 모든 side effect가 적용됐다는걸 보장해주는 지점
semicolon, end of full expression

end of full expression이란 말그대로 subexpression이 아닌 큰 expression 전체의 끝을 말한다.
예를들어 expression statement의 expression 부분(세미콜론 이전까지 말하는듯),
while loop의 test condition으로 쓰이는 exrpession 등이 있다.

C++11에서는 sequence point라는 용어를 사용하지 않는다.
multithreaded programming을 잘 다루기위해 다른 방식으로 설명하는데,
어떤 events가 다른 events 이전에 being sequenced됐다고 설명한다.
(자세한건 멀티쓰레드 저거 배우고 보는게 나을듯)

Compound Statements, or Blocks

compound statement(block)은 C에서 배웠듯이 statements 하나로 묶어주고, 내부에서 쓰인 변수는 block 끝나면 deallocate되고, 같은 이름 변수 나오면 밖의 애는 hide되고...

comma operator
for문 괄호안이라던가 두개 이상 statement가 필요할때 하나의 statement로 묶어준다.(block이랑 같은 효과)
모든 comma가 comma opeator는 아니다. int i, j; 여기의 comma는 그냥 변수 이름 구분해주는 용도

C++에선 comma operator에 대해 아래 특징도 보장
1. 첫번째 expression이 두번째보다 먼저 계산된다. 즉, comma operator는 sequence point
2. 두번째 expression의 결과가 해당 expression의 결과이다.(여러 comma operator로 묶였으면 당연히 제일 오른쪽 놈이겠지. 두번째의 두번째의...으로 가니까)

Relational Expressions

relational operator들은 당연히 C-style string에선 작동 X
string object에 대해선 작동 O
string object와 C-style string간에도 작동 O

각 relational expression은 bool value를 산출


Time-Delay Loop

화면에 메시지를 띄우고 읽을때까지 기다린다던가 할때 사용된다.

예전 방식)
computer processor speed 올라가면 수정해야된다는 문제점
심지어 요즘 컴파일러는 쓸모없는 loop라고 판단되면 wait을 10000으로 세팅하고 loop skip해버린다.

long wait = 0;
while (wait < 10000)
	wait++;

system clock 사용)
ctime(time.h) heade의 clock() 함수 사용

문제점 1. clock()이 시간을 꼭 반환하진 않는다.(system time unit을 반환하던가)
문제점 2. clock() 함수가 어디선 long반환하고 어디선 unsigned long 반환하고.. 반환 type이 일관적이지 않다.

해결법 1. ctime(time.h) header의 CLOCKS_PER_SEC symbolic constant를 사용한다. 초당 system time unit의 수를 정의하는 상수이다.
그럼 결과를 이거로 나누면 초가 나오겠지?(반대로해도되고)
해결법 2. clock_t type을 사용한다. clock() 함수의 return type에 맞도록 정의된다.

ex.

#include <iostream>
#include <ctime>
int main() {
	using namespace std;
    cout << "Enter the delay time, in seconds: ";
    float secs;
    cin >> secs;
    clock_t delay = secs * CLOCKS_PER_SEC;
    
    cout << "starting\n";
    clock_t start = clock();
    while (clock() - start < delay) ;
    
    cout << "done\n";
    return 0;
}

while (clock() - start < delay) ; 이게 좀 주목할만 한듯


do while loop

exit-condition loop

대부분은 entry-condition이 더 낫지만,
입력을 먼저 받고 그걸 test해야한다던가 하는 경우엔 do while문이 더 낫다.
for(;;) {~} 형식의 무한반복문인데 안에서 break로 빠져나오는 경우도 do while이나 while로 보기쉽게 바뀔 수 있다.


The Range-Based for Loop (C++11)

container classes 중 하나의 type이나(vector라든가) built-in array type에 대한 loop 작업을 단순화한다.

double prices[5] = {4.99, 10.99, 6.87, 7.99, 8.49};
for (double x : prices)
	cout << x << std::endl;

xprices 배열의 첫번째 element부터 쭉 assign된다.
for (int x : {3, 5, 7, 6, 9}) ~~~~ 식으로 initialization list만 사용해도 된다.

값을 변경하고 싶다면 for (double &x : prices) 식으로 해야한다.(값이 그냥 x에 복사되는 방식인가 보네)


C++ 문자 입력받기 (Text Input)

unadorned cin

do {
	cin >> ch;
    ++count;
    cout << ch;
} while (ch != '#');

문제점)
문자들을 입력받아서 '#'이전까지 출력하는데,
cin은 enter키를 입력해야 한번에 program으로 보내진다. 한글자 한글자 입력받으며 반응하는게 아니라. 그래서 #다음으로도 계속 글을 쓸 수는 있다.(화면에 출력은 안되지만)
(buffering : 상대적으로 느린 곳으로 공간에 접근할때 buffer를 이용해 해결)

space를 입력하면 출력이 안되고 그냥 무시되는데, 그 이유는 그냥 cin이 space를 skip하기 때문이다.

cin.get(char)

istream class의 member 함수인 cin.get(ch)은 sapce를 무시하지 않는다. (string 입력받을때도 그랬었음)
cin.get(ch);

cin처럼 buffer되긴한다. 그래서 #지나서 계속 입력해도 되고, 엔터쳐서 한번에 program으로 보내짐

get함수에 ch를 넣는게 이상할 수 있다.
C에선 분명 ch의 값을 바꾸려면 주소를 주거나 해야했는데?
C++에선 reference라는 type을 추가해서 이를 해결했다.
함수 선언할때 argument를 reference type으로 선언하면 저렇게 사용 가능

지금까지 배운 cin.get 함수만해도 3종류인데, 이걸 function overloading이라고 함.
각 함수는 arugument list를 보고 구별해서 적용됨.

The End-of-File Condition

file로 input을 받는다면 EOF를 탐지할 수 있다.
C++ input 기능들은 OS와 협력?하여 input이 file 끝에 도달했는지 탐지한다.
cin이 EOF를 탐지하면 eofbitfailbit1로 set 한다.
cin.eof() : EOF가 탐지되면 bool값 true 반환, 아니면 false
cin.fail() : eofbit이나 failbit이 세팅되면 true 반환, 아니면 false

C에서 redirection을 이용해(ex.gofish <fishtable) 표준 stream을 수정하고, pritnf나 scanf를 적용할 수 있었던 것처럼, C++에서도 같은 방식으로 cin에 바로 입력받을 수 있다.

각 OS에서 EOF condition을 키보드를 이용해 입력할 수 있다. Ctrl+D, Ctrl+Z, Ctrl+Z 이후 Enter 등등

EOF 탐지되면 cin내의 flag가 set되고, 추가 cin은 아무 효과가 없다.
file에선 당연히 EOF 만나면 더 이상 안읽는게 맞지만,
keyboard로 입력한거면 뒤에도 더 입력받을 수 있으니 cin.clear()로 이를 해결할 수 있다.
(몇몇 system에선 cin.clear()로 해결 안될수도 있음)

EOF 이해: 첫번째 답변
end-of-file의 구현 방식은 다 다르겠지만 그냥 이렇게 약속된 것(p.242 Tip)
위키피디아도 보면 좋다

Idioms (Character Input)

while (cin.fail() == flase) //test for EOF : 기본
while (!cin.fail()) //input 성공하는동안 반복

istream class는 istream object를 bool type으로 바꾸는 함수를 제공, bool이 필요한 위치에 자동으로 변경된다.
마지막 read가 success였다면 bool type true로 변환되고, 아니면 false로 변환
while (cin) : input 성공하는 동안 반복 (물론 이건 test만하는거고 input문은 따로 있어야 함.)
이렇게 쓰는게 disk failure같은 다른 오류도 감지하기때문에 더 낫다.

그래서 cin.get(ch)는 cin object를 반환하므로 아래와 같이 쓸 수 있다.
while (cin.get(ch))

ch = cin.get()

cin.get()은 다음 character를 읽어와 반환(바로 위처럼 cin 반환하는게 아님)
EOF일땐 EOF symbolic constant(iostream file에 정의됨)를 반환한다. 그래서 cin.get()을 쓰려면 받아올 변수를 int로 선언하는게 좋다.(cin.get(ch)는 EOF 반환안함)

ch = cin.get(); 이렇게 C의 getchar()처럼 쓸 수 있다.
cout.put(ch); 이렇게 C의 putchar()처럼 쓸 수 있다.

put(ch)에 int값 ch를 넣어도 대부분 잘 변환된다.
put함수를 put(char), put(unsigned char), put(signed char) 이렇게 세가지로 정의하는 implementation에선
int를 넣었을때 int를 뭐로 변환할지 선택지가 3개나되기때문에 error message 출력된다.

cin.get(ch) VS ch=cin.get()

특징cin.get(ch)ch=cin.get()
input 문자 전달 방식ch에 assign함수 return값 이용해서 assign
return값(문자일때)istream class의 object(bool 변환시 true)해당 문자의 code int 값
return값(EOF일때)istream class의 object(bool 변환시 false) / 대신 flag 세팅EOF

character 입력받는거라 EOF만 아니면 문제될게 없음. 그냥 그대로 다 읽어옴.


2차원 배열

C++에선 특별한 2차원 배열 type을 제공하진 않고, 그냥 1차원배열안에 1차원배열을 만들어야한다.(C도 그렇지)

문자열 배열 저장할때 char*의 배열도 좋지만, stirng object의 배열도 C++에선 가능


영단어

consolidate 강화하다
extrapolate 추론하다
synthesize 합성하다
distort 비틀다/왜곡하다
steer 조종하다
paean 찬가(승리의 노래)
peculiar 이상한
bending 굽힘
strip 벗다
odd 확률 (홀수, 이상한 말고 다른 뜻 있길래 적어둠)
palindrome 회문 (madam 같이 뒤에서 읽어도 같은 단어나 구)
nifty 훌륭한
stash (안전한 곳에)넣어두다
analogously 비슷하게
tidbit(=titbit) 한조각, 작은 흥미로운 소식
relent (거부하다가) 동의하다
relentless 끈질긴
reduce to ~상태로 몰아넣다
egregious 지독한
march 행진하다, 3월
endearing 사랑스러운
unadorned 꾸밈없는
obliterate 지우다
legitimate 적당한, 적법한
nostalgic 향수를 불러일으키는
yearn 갈망하다, 동경하다

0개의 댓글