배움 저장소

[홍정모의 따라하며 배우는 C++] 5.흐름제어 본문

Programming Language/C++

[홍정모의 따라하며 배우는 C++] 5.흐름제어

시옷지읏 2021. 12. 17. 16:13

5.1 제어 흐름 개요 (Control Flow)


- 프로그래밍은 CPU에게 특정 명령을 실행시킴을 말한다.

- 이때 코드의 묶음을 다양한 흐름으로 실행할 수 있다. 이를 Control Flow라 하고 5가지 종류가 있다.

종류 Keyword
Halt(중단)  
Jump(점프) goto, break, continue
Conditional Branches(조건 분기) if, switch
Loops(반복) while, do while, for
Exceptions(예외 처리) try, catch, throw

 

exit( ) vs return

- <cstdlib>라이브러리에서 exit(int) 함수를 사용하여 프로그램을 긴급하게 종료시킬 수 있다. 이때 Parameter를 운영체제에게 전달하며 디버깅 용도로 사용된다.

- return keyword는 자료형에 맞는 반환값을 전달하고 해당 함수가 종료된다. 

 

5.2 조건문 if


if ( expression )를 잘못 사용한 사례

- if(x = -1)에서 x=-1이 실행되고 if(x) 조건문을 실행하여 항상 참이된다.

int x = 0 ;
if (x = -1)
{
    cout << "x=" << x << endl;
}
cout << "x is " << x << endl;
>> x=-1
>> x is -1

 

5.3 switch-case


break keyword가 없으면 다음 case를 실행시킨다. 의도하였다면 주석을 남기자

int x = 0;
switch (x)
{
case 0:             // work with case 1
    cout << "Zero"; // no break on 0
case 1:
    cout << "One";
    break;
case 2:
    cout << "Two";
    break;
}
cout << endl;

 

enum class와 함께 사용하는 switch-case

- switch-case는 enum(열거형) 혹은 enum class(영역제한 열거형)과 많이 쓴다. 정수 x를 명시적 변환하여 사용하였다.

enum class Colors
{
    BLACK,
    WHITE,
    RED,
    GREEN,
};

int main()
{
    int x;
    cin >> x;

    switch ((Colors)x)
    {
    case Colors::BLACK:
        cout << "BLACK";
        break;
    case Colors::WHITE:
        cout << "WHITE";
        break;
    case Colors::RED:
        cout << "Red";
        break;
    case Colors::GREEN:
        cout << "Green";
        break;
    default:
        cout << "Undefined Number " << x << endl;
    }
}

이 때 case마다 class Tag를 붙여줌이 번거로울 수 있다. 아래의 예제를 보자.

 

static_cast를 활용하여 enum class 변수를 간략하게 표시할 수 있다

Colors col = Colors::BLACK;

switch (static_cast<int>(col))
{
case 0:
    cout << "BLACK";
    break;
case 1:
    cout << "WHITE";
    break;
case 2:
    cout << "Red";
    break;
}

 

switch-case에서 변수 초기화/선언하기

 case 분기 이전 변수를 선언할 수 있다

switch (x)
{
    int a;
case 0:
    a = 1;
    break;
}

 

case 분기 이후에 변수를 선언하여도 내부적으로 case 분기 이전에 선언된다

int x = 1;

switch (x)
{
case 0:
    int a;
    break;
case 1:
    a = 111;
    cout << a << endl;
    break;
}
>> 111

 

switch-case 내부에서 변수를 초기화 할 수 없다. 반드시 선언 이후 값을 할당하자

- 아래 코드는 다음과 같은 에러메세지를 출력한다. error C2360: initialization of 'a' is skipped by 'case' label

int x = 1;

switch (x)
{
    int a = 1;
case 0:
    break;
}

case분기문이 1개일 때 초기화 할 수 있다. 2개 이상일 때는 초기화 할 수 없다

- 위와 동일한 에러메세지를 출력한다.

int x = 1;

switch (x)
{
case 0:
    int a = 1;
    break;

case 1:
    cout << "Work!";
}

 

switch-case에서 초기화 하고 싶다면 { }로 새로운 Scope(영역)을 만들어주자

int x = 0;

switch (x)
{
case 0:
    {
        int a = 1;
        cout << a << endl;
        break;
    }
case 1:
    cout << "Work!";
    break;
default:
    cout << "Undefined Input " << x << endl;
}

- default keyword는 정의되지 않은 case일 때 실행된다. break를 넣지 않아도 된다.

 

Visual Studio의 자동완성 기능 활용하기

- < switch문 자동완성 > 'switch' 입력 → (tab) → enum class 이름 (위 예제에서는 'Colors')입력 → 다른곳 클릭

 

5.4 goto

잘 사용하지 않는다

 

5.5 반복문 while


- while 반복문 내부 body에 사용자 정의 자료형을 정의/선언하면 느려질 수 있다. 외부에서 정의/선언 하자.

- unsigned int가 최적화에 유리하다

 

5.6 반복문 do-while

do-while반복문은 한 번은 반드시 실행시킨다

5.7 반복문 for

 

5.8 break, continue


do while 반복문의 잘못된 사용예제

- 아래 코드는 Infinite Loop에 빠진다.

    int count(0);
    do
    {
        if(count == 2) continue;
        count++;
        cout << count << endl;
    }while(count < 5);

(1) if 조건문의 순서를 바꾸어주면 해결된다.

    int count(0);
    do
    {
        count++;
        cout << count << endl;
        if(count == 2) continue;

    }while(count < 5);

(2) while(expression) 내부에 증감연산자를 넣으면 해결된다.

    int count(0);
    do
    {
        if(count == 2) continue;
        cout << count << endl;

    }while(++count < 5);

 

5.9 난수 만들기 random numbers


난수 생성 직접 구현해보기

- seed를 static으로 지정하자. 함수가 실행될 때마다 초기화 됨을 막아준다. 효율적이다.

- 아래와 같은 방법으로 난수를 생성할 수 있다. 실전에는 standard library를 사용한다.

unsigned int GetRand()
{
    static unsigned int seed = 1234;
    seed = 23467823 * seed + 438057;
    return seed % 23530;
}

int main()
{
    for (int i = 0; i < 10; ++i)
        cout << GetRand() << endl;
}
12823
5890
19301
7786
15717
4252
5055
21032
10469
5552

 

<cstdlib>라이브러리 활용하기

- 아래 함수는 몇 번을 반복하여도 동일한 결과값이 나온다. seed가 동일하면 난수 생성 규칙이 동일하다. 

#include <iostream>
#include <cstdlib> // std::radn(), std::srand()

using namespace std;

int main()
{
    std::srand(1234); // seed
    for (int i = 0; i < 10; ++i)
        cout << std::rand() << endl;
}
4068
213
12761
8758
23056
7717
15274
24508
4056
13304

난수 규칙을 랜덤으로 바꾸어주기 위하여 <ctime>라이브러리를 활용해보자

#include <iostream>
#include <cstdlib> // std::radn(), std::srand()
#include <ctime>

using namespace std;

int main()
{
    std::srand(static_cast<unsigned int>(time(0))); // seed
    for (int i = 0; i < 10; ++i)
        cout << std::rand() << endl;
}

 

최소값과 최대값 사이에서 난수 생성해보기

int getRandomNumber(int min, int max)
{
    static const double fraction = 1.0 / (RAND_MAX + 1.0); // much faster

    return min + static_cast<int>( (max - min + 1 ) * (std::rand() * fraction));
}

 

<random>라이브러리 활용하기

(1) random_device로 random에 필요한 정보를 담은 객체를 생성한다.

(2) seed를 생성한다.

(3) 분포를 설정한다.

(4) 설정한 분포값 사이에서 난수를 생성한다.

#include <iostream>
#include <random>
using namespace std;

int main()
{
    std::random_device rd; // object for random implementation
    std::mt19937_64 mesennce(rd()); // seeding, mesennce is mathematician
    std::uniform_int_distribution<>dice(1,6); // set distribution
                                              // all possiblity adjusted equal

    for(int i=0; i<10; ++i)
        cout << dice(mesennce) << endl;
}

 

5.10 std::cin 더 잘 쓰기


아래는 덧셈 뺄샘 계산기 코드이다. 예외처리까지 구현되었다

- 아래에서 cin.clear()함수는 cin 객체의 flag를 초기화 시킨다. cin 객체는 입력받을 때 EOF, 에러발생, 입력 성공의 경우를 각각 비트 플래그로 저장한다.

#include <iostream>
using namespace std;

int getInt()
{
    while (true)
    {
        cout << "Enter an Integer Number : ";
        int x; 
        cin >> x;

        if (std::cin.fail()) // ex) if x get over INTMAX, it makes infitite loop 
        {
            std::cin.clear(); // clear everything in "control in"
            std::cin.ignore(32767, '\n'); // ignore first parameter's buffered
                                         // or ignore till meet '\n' character
            cout << "Invalid number, please try again" << endl;
        }
        else
        {
            std::cin.ignore(32767, '\n');
            return x;
        }
    }
}

void printResult(int x, char op, int y)
{
    if(op == '+') cout << x+y << endl;
    else if(op == '-') cout << x-y << endl;
    else
    {
        cout << "Invalid operator" << endl;
    }
}

char getOperator()
{
    while (true)
    {
        cout << "Enter an operator(+, -) : ";
        char op;
        cin >> op;
        std::cin.ignore(32767, '\n');

        if(op == '+' || op == '-')
            return op;
        else
            cout << "Invalid Operator, please try again" << endl;
    }
}

int main()
{
    int x = getInt();
    int y = getInt();
    char ch = getOperator();
    printResult(x,ch,y);
}
Comments