for 반복문

for 반복문

반복할 문장이 반복 조건을 직접 관리하지 않아도 되도록, 조건에 따라 문장을 반복 실행하는 for 반복문을 알아볼게요. 반복 횟수를 세는 카운터 변수와 반복 조건을 한 곳에 모아 두는 문장입니다.

출처: cppreference

본문

문법

    attr (optional) for ( init-statement condition (optional) ; expression (optional) ) statement

| attr | - | (C++11부터) 개수 제한 없는 속성 | | init-statement | - | 다음 중 하나: 표현식 문장(널 문장 ;일 수도 있음), 단순 선언(대개 이니셜라이저가 있는 루프 카운터 변수 선언이며, 임의의 많은 변수나 구조적 바인딩(structured binding)을 선언할 수 있어요, C++17부터), 또는 별칭 선언(alias declaration, C++23부터). 어떤 init-statement든 세미콜론으로 끝나야 한다는 점을 기억하세요. 그래서 흔히 '세미콜론이 뒤따르는 표현식이나 선언'으로 비공식적으로 표현됩니다. | | condition | - | 조건(condition) | | expression | - | 표현식(대개 루프 카운터를 증가시키는 표현식) | | statement | - | 문장(대개 복합문) |

조건 (Condition)

조건은 표현식이거나 단순 선언(simple declaration)일 수 있어요.

  • 문법적으로 구조적 바인딩 선언으로 해석될 수 있으면 구조적 바인딩 선언으로 해석돼요. (C++26부터)
  • 문법적으로 표현식으로 해석될 수 있으면 표현식으로 처리돼요. 그렇지 않으면 구조적 바인딩 선언이 아닌 선언으로 처리됩니다. (C++26부터)

제어가 조건에 도달하면 조건은 값을 산출하며, 이 값으로 statement가 실행될지가 결정돼요.

표현식 조건

조건이 표현식이면, 산출되는 값은 그 표현식의 값을 문맥적으로 bool로 변환한 값이에요. 그 변환이 ill-formed이면 프로그램도 ill-formed이 됩니다.

선언 조건

조건이 단순 선언이면, 산출되는 값은 결정 변수(decision variable, 아래 참고)의 값을 문맥적으로 bool로 변환한 값이에요. 그 변환이 ill-formed이면 프로그램도 ill-formed이 됩니다.

구조적 바인딩이 아닌 선언

선언에는 다음 제약이 있어요:

  • 문법적으로 다음 형태를 따릅니다: type-specifier-seq declarator = assignment-expression (C++11 이전), attribute-specifier-seq (optional) decl-specifier-seq declarator brace-or-equal-initializer (C++11부터)
  • declarator는 함수나 배열을 지정할 수 없어요.
  • 타입 지정자 시퀀스(C++11 이전)는 타입 지정자와 constexpr만 포함할 수 있으며, 선언 지정자 시퀀스(C++11부터)는 클래스나 열거형을 정의할 수 없어요.

선언의 결정 변수는 선언된 변수예요. (C++26부터) 구조적 바인딩 선언의 경우 이니셜라이저의 표현식이 배열 타입일 수 없고, 결정 변수는 선언이 도입하는 가상 변수(invented variable) e예요.

설명

for 문은 다음의 등가물이에요:

    { init-statement while ( condition ) { statement expression ; } }

단, 다음이 다릅니다:

  • init-statement의 스코프와 condition의 스코프는 같아요.
  • statement의 스코프와 expression의 스코프는 서로 겹치지 않으며, init-statementcondition의 스코프 안에 중첩돼요.
  • statement에서 continue 문을 실행하면 expression이 평가돼요.
  • 빈 조건은 true와 동등해요.

루프를 statement 안에서 끝내야 한다면 break 문을 종료 문장으로 쓸 수 있어요. 현재 반복만 끝내고 싶다면 continue 문을 지름길로 쓰면 됩니다.

참고 사항

while 루프와 마찬가지로, statement가 복합문이 아니면 그 안에서 선언된 변수의 스코프는 복합문인 것처럼 루프 몸통으로 제한돼요.

for (;;)
    int n;
// n goes out of scope

C++의 forward progress guarantee의 일부로, 관찰 가능한 동작이 없는(관찰 가능한 동작 없는 루프가 trivial infinite loop가 아닌 경우, C++26부터) 루프가 종료되지 않으면 동작은 정의되지 않아요(undefined behavior). 컴파일러는 그런 루프를 제거할 수 있습니다.

C에서는 init-statementcondition의 스코프에서 선언된 이름이 statement의 스코프에서 가려질(shadow) 수 있지만, C++에서는 금지돼요:

for (int i = 0;;)
{
    long i = 1;   // valid C, invalid C++
    // ...
}

키워드

for

예제

단순한 카운터 루프부터 다양한 형태의 for 사용법을 하나씩 확인해 볼게요.

#include <iostream>
#include <vector>

int main()
{
    std::cout << "1) typical loop with a single statement as the body:\n";
    for (int i = 0; i < 10; ++i)
        std::cout << i << ' ';
    
    std::cout << "\n\n" "2) init-statement can declare multiple names, as\n"
                 "long as they can use the same decl-specifier-seq:\n";
    for (int i = 0, *p = &i; i < 9; i += 2)
        std::cout << i << ':' << *p << ' ';
    
    std::cout << "\n\n" "3) condition may be a declaration:\n";
    char cstr[] = "Hello";
    for (int n = 0; char c = cstr[n]; ++n)
        std::cout << c;
    
    std::cout << "\n\n" "4) init-statement can use the auto type specifier:\n";
    std::vector<int> v = {3, 1, 4, 1, 5, 9};
    for (auto iter = v.begin(); iter != v.end(); ++iter)
        std::cout << *iter << ' ';
    
    std::cout << "\n\n" "5) init-statement can be an expression:\n";
    int n = 0;
    for (std::cout << "Loop start\n";
         std::cout << "Loop test\n";
         std::cout << "Iteration " << ++n << '\n')
    {
        if (n > 1)
            break;
    }
    
    std::cout << "\n" "6) constructors and destructors of objects created\n"
                 "in the loop's body are called per each iteration:\n";
    struct S
    {
        S(int x, int y) { std::cout << "S::S(" << x << ", " << y << "); "; }
        ~S() { std::cout << "S::~S()\n"; }
    };
    for (int i{0}, j{5}; i < j; ++i, --j)
        S s{i, j};
    
    std::cout << "\n" "7) init-statement can use structured bindings:\n";
    long arr[]{1, 3, 7};
    for (auto [i, j, k] = arr; i + j < k; ++i)
        std::cout << i + j << ' ';
    std::cout << '\n';
}

출력:

1) typical loop with a single statement as the body:
0 1 2 3 4 5 6 7 8 9

2) init-statement can declare multiple names, as
long as they can use the same decl-specifier-seq:
0:0 2:2 4:4 6:6 8:8

3) condition may be a declaration:
Hello

4) init-statement can use the auto type specifier:
3 1 4 1 5 9

5) init-statement can be an expression:
Loop start
Loop test
Iteration 1
Loop test
Iteration 2
Loop test

6) constructors and destructors of objects created
in the loop's body are called per each iteration:
S::S(0, 5); S::~S()
S::S(1, 4); S::~S()
S::S(2, 3); S::~S()

7) init-statement can use structured bindings:
4 5 6

더 알아보기 (Learn more)