C++/카루의 C++ 강좌

[카루의 C++ 강좌] 3-3. 반복문의 중첩, 무한반복과 break

카루-R 2020. 3. 24. 18:09
반응형

반복문의 중첩

3-1에서 배웠던 반복문 for, while, do~while문을 기억하시나요? 앞으로는 do~while문은 거의 쓰지 않고 for과 while을 주로 쓸 것입니다. (그래서인지 구글은 Go언어에서 반복문을 for만 사용합니다...)

이 반복문은 중첩이 가능합니다. 구구단을 출력하거나, 흔히 예제로 많이 쓰이는 "별 찍기"도 반복문을 응용하는 것입니다.

#include <iostream>

int main()
{
    for (int i{2}; i <= 9; i++)
    {
        for (int j{1}; j <= 9; j++)
        {
            std::cout << i << " * " << j << " = " << i * j << std::endl;
        }
    }
}
2 * 1 = 2
2 * 2 = 4
......(생략)
4 * 9 = 36
5 * 1 = 5
5 * 2 = 10
......(생략)
9 * 8 = 72
9 * 9 = 81

아주 깔끔하게 구구단이 출력되었습니다. for문에서 문장이 한 줄이면 중괄호를 생략할 수 있다고 말씀드렸습니다.

그리고, C++20에 추가된 std::format()을 이용하면 (2020년 3월 현재 기준으로 이를 지원하는 컴파일러가 없습니다!) 더 간결하게 작성이 가능합니다.

#include <iostream>
#include <format>

int main()
{
    for (int i{2}; i <= 9; i++)
        for (int j{1}; j <= 9; j++)
            std::cout << std::format("{} * {} = {}", i, j, i * j) << std::endl;
}

왜 줄어든 것 같지가 않을까?

보시다시피 for문 안에 for문이 들어가있는 구조입니다. 안쪽 for문을 모두 실행하면 바깥쪽 for문의 한 사이클이 실행되는 격입니다. 따라서 std::cout의 반복 횟수는 (바깥쪽 for문 횟수) * (안쪽 for문 횟수)가 됩니다.

자, 아래처럼 출력하고 싶다면 어떻게 해야 할까요?

*
**
***
****

중첩 반복문에서 빠질 수 없는 예제가 바로 이런 '별 찍기'입니다.

물론 아래와 같은 방법을 이용할 수도 있습니다.

std::cout << "*\n**\n***\n****";

그런데 이렇게 하면 지금 당장의 문제는 풀 수 있어도 앞으로의 문제는 풀 수 없겠죠. 생각하는 힘을 길러야 합니다.

자, 우선 위와 같은 형태는 * 문자 하나씩만 출력하는 게 편합니다. 대신 여러번 출력하죠.

일단 줄에 네 줄이니 바깥쪽 for문은 총 4번 반복한다고 볼 수 있습니다. 코드를 작성하죠.

for (int i{1}; i <= 4; i++)

여기서 i의 값은 i = 1, 2, 3, 4로 변화합니다. 어라? 그렇다면 각 줄에서 i의 값이 출력해야 할 별의 개수와 같습니다. 다시말해 1번 줄에 별 한 개, 2번 줄에 두 개... 이런 식으로요.

자, 그럼 i의 개수만큼 출력을 하면 됩니다.

    for (int j{1}; j <= i; j++)

for (int j{1}; j <= i; j++)

자, 안쪽 for문도 됐습니다. 출력은 아까 말한 대로 * 문자 하나씩만 출력합니다. std::cout의 << 연산자를 사용해도 되지만 기능이 더 간단한 put() 함수를 이용하겠습니다. 매개변수로 char 문자를 받습니다..

(fallbit를 설정하지 않는다는 차이가 있습니다)

        std::cout.put('*');

안쪽 for문이 끝나면 개행도 해 줘야 합니다. 자, 그럼 코드를 종합해 보겠습니다.

#include <iostream>

int main()
{
    for (int i{1}; i <= 4; i++)
    {
        for (int j{1}; j <= i; j++) 
            std::cout.put('*');
  
        std::cout << std::endl;
    }
}

중괄호를 생략할 경우엔 들여쓰기를 확실하게 하셔야 합니다. 한 문장만 for문에 포함되기 때문입니다.

출력 결과는 이미 위에 있으니 생략합니다. 줄의 수를 바꾸고 싶다면 바깥쪽 for문의 4를 5, 6..등으로 변경해 보세요.

 

무한 반복문

무한 반복은 말 그대로 반복문을 계속 실행하는 것입니다. 당연히 프로그램도 멈추지 않고 계속 동작하게 됩니다.

for문이나 while문을 통해서 만들 수 있는데, 모습은 다음과 같습니다.

for (;;) {   }
while (true) {   }

for문은 가운데의 조건식이 생략되면 무한반복입니다. while문은 조건식이 항상 참이면(=true를 넣으면) 무한 반복입니다. 원하신다면 for(;;)을 써서 무한 반복을 표현해도 됩니다만, 저는 개인적으로 while(true)를 더 자주 사용합니다. 세미콜론이 조금... 안 예뻐서 그렇습니다.

참고로, while(true)를 쓰면 경우에 따라 조건식이 항상 참이라는 경고를 뿜는 컴파일러가 있을 수 있습니다. 따라서 이 강좌에서는 for (;;)만 쓰도록 하겠습니다.

코딩 스타일때문에 얘기가 길어지네요. 저는 가독성 때문에 while (true)를 주로 쓰고, 여러분들께서도 원한다면 while을 사용하셔도 됩니다.

for (;;) {
    std::cout << "Hello, world!" << std::endl;
}

이건 어떻게 출력이 될까요? 네, 프로그램을 강제 종료하기 직전까지 계속 출력됩니다.

참고로, 저번에 배운 define을 응용해도 됩니다.

#include <iostream>
#define infinite for (;;)

int main() 
{
    infinite
    {
        std::cout << "Hello, world!" << std::endl;
    }
}

뭐 굳이 이렇게까지 하..실진 모르겠지만 일단 이렇게도 할 수 있다는 점 알려드립니다.

 

반복문 탈출, break

break는 반복문을 말 그대로 break 하기 위해 사용합니다. 즉, 탈출 코드이죠.

예제를 봅시다.

for (char ch{};;)
{
    std::cout << "반복을 끝내려면 x를 입력하세요." << std::endl;
    std::cin >> ch;
    if (ch == 'x' || ch == 'X')
        break;
}

메인 함수에 위의 for문을 집어넣고 직접 실행해보시기 바랍니다. 다른 문자를 막 입력하다가 x를 입력하면 무슨 일이 일어나는지 관찰해보세요.

위는 무한반복문입니다. 단지 ch라는 변수를 선언했을 뿐입니다. 그런데 값을 입력받고, if문을 통해 그 값이 대문자 X "또는" 소문자 x 이면 break;를 합니다. 즉, 반복문을 나오게 되는거죠.

참고로 break는 해당 문장이 있는 반복문 하나만 탈출합니다. 즉,

for (;;)
{
    while (true)
    {
        for (;;) // (3) 즉 여기서 계속 무한반복을 하죠.
        {
             for (;;)
             {
                  break; // (1) 여기서 break를 하면
             }
        } // (2) 여기로 탈출합니다.
    }
}

이건 break;를 사용하긴 했지만 가장 안쪽의 for문만 탈출하고 그 바깥의 for문에 무한반복이 걸리게 됩니다. 이런 건 쉽게 탈출하기가 어려우니 다른 방법을 써야 합니다.

 

중첩 반복문을 탈출하려면 어떻게 해야 할까요?

"[C++ 심화] 7. 금단의 goto문"을 참고해 주세요!

 

무한 반복이 아니더라도 break는 사용할 수 있습니다. 아래 코드는 1000 이하의 짝수만 더하다가 그 합이 200을 넘으면 종료하는 코드입니다.

#include <iostream>

int main() {
    int sum{};
                            /* 2씩 증가 */
    for (int i{2}; i <= 1000; ++++i) {
        sum += i;
        if (sum > 200)  // 200을 넘으면
            break;      // for문 탈출
    }
    std::cout << sum; // 210이 출력됨
}

sum이 200을 넘긴 순간 if문이 발동하여 break가 됩니다.

자, 그런데 증감식에서 ++++i라고 쓴 것 보이시나요? 이 부분을 continue를 사용하여 다르게 표현할 수도 있습니다.

#include <iostream>

int main() {
    int sum{};
    for (int i{}; i <= 1000; i++) {
        if (i % 2 == 1) // i가 홀수라면
            continue;   // 더 이상 진행하지 않고 바로 증감식으로 이동

        sum += i;

        if (sum > 200)
            break;
    }
    std::cout << sum; // 210이 출력됨
}

새로운 키워드가 등장했습니다, continue. 이건 마치 for문의 (while문의) 닫는 중괄호화 같은 역할을 합니다. 반복문 안에서 continue문을 만나면 아래의 코드를 더 이상 실행하지 않고 바로 증감식으로 이동합니다. 해당 사이클을 거기서 끝낸다는 의미이죠.

즉, 반복문의 닫는 중괄호 앞엔 continue가 생략되어있다고 보셔도 됩니다.

 

3-3. 도전 과제

1. 중첩 반복문을 사용하여 다음을 출력하세요.

###
##
#

 

2. 주석을 잘 읽고, ____ 안에 알맞은 말을 넣어보세요.

// 입력된 수가 50보다 크면 반복문을 탈출하려고 합니다.
// 정수형 변수 num이 이미 선언된 상태입니다.
while (true)
{
    if (std::cin >> num; _____________)
        ______;
}

 

3. 중첩 반복문을 사용하여 다음을 출력하세요.

  *  
 *** 
*****

 

4. [고난이도] 중첩 반복문을 사용하여 다음을 출력하세요.

   @   
  @@@  
 @@@@@
@@@@@@@
 @@@@@
  @@@
   @

수고하셨습니다. 이번 내용은 조금 어려운 게 많으니 특히 중첩 반복문 위주로 공부해 보세요.

아마 4번 문제가 조금 어려우실 겁니다.

참고: 본 강좌의 연계 심화 강좌는

"[C++ 심화] 7. 금단의 goto문"입니다.

반응형