for

ifgoto만을 사용해 루프를 제어하는 것은 아주 힘든 일입니다. 놀랍게도, 구석기 시대 프로그래밍 언어인 BASIC이나 어셈블리 언어에서는 이런 것들만 사용해서 루프를 제어했다고 합니다(요즘 BASIC에는 있는 모양입니다만). 그러나 애석하게도, 우리는 이런 것에 쏟을 시간이 없습니다. 그래서 나온 것이 for입니다.

for를 사용하면 루프를 쉽게 만들 수 있습니다. for(초기화; 조건식; 증감문) { 내용 }의 형식으로 for문을 만들 수 있습니다. 초기화에서는 루프 안에서 사용할 변수를 초기화합니다. 조건식은 루프 안쪽 코드가 실행되기 전에 실행되며, 참이 아니면 루프를 종료합니다. 이 식은 초기화 직후에도 평가될 수 있습니다. 증감문은 루프 안쪽 코드가 실행된 후에 실행되는 문장으로, 보통 루프 변수를 증감시키는 용도로 쓰입니다.

goto는 여러 가지 용도로 사용되었지만, for는 오로지 루프만을 위해 사용되므로 가독성도 좋고, 컴파일러가 최적화해 줄 여지도 많습니다. 또, (정상적으로 사용한다는 가정 하에) for가 있는 한 줄만 봐도 이 루프가 얼마나 돌지 알 수 있기 때문에 여러모로 goto보다 좋습니다.

#include <stdio.h>
int main() {
      int k, i;
      scanf("%d", &k);
      for(i = 0; i < k; ++i) {
            printf("i: %d\n", i);
      }
      return 0;
}

4

i: 0
i: 1
i: 2
i: 3

여기서의 루프 변수는 i입니다. 관습적으로, 루프 변수는 ij같은 것을 많이 사용합니다.

goto와 레이블을 사용하면 이런 내용이겠지요.

#include <stdio.h>
int main() {
      int k, i;
      scanf("%d", &k);
      i = 0;
loop_start:
      if(!(i < k))
            goto loop_end;
      printf("i: %d\n", i);
      ++i;
      goto loop_start;
loop_end:
      return 0;
}

for 문을 사용한 쪽이 훨씬 낫지 않습니까?

if와 마찬가지로, 한 줄 뿐이라면 중괄호를 생략할 수 있습니다. 이는 뒤의 whiledo ~ while에도 해당되는 내용입니다.

#include <stdio.h>
int main() {
      int k, i;
      scanf("%d", &k);
      for(i = 0; i < k; ++i)
            printf("i: %d\n", i);
      return 0;
}

for 문과 함께 변수를 선언할 수도 있습니다.

#include <stdio.h>
int main() {
      int k;
      scanf("%d", &k);
      for(int i = 0; i < k; ++i)
            printf("i: %d\n", i);
      return 0;
}

다만, 이때의 ifor 문 안에서만 사용할 수 있습니다.

콤마를 사용해 여러 개의 초기화와 증감문 사용도 가능하고,

#include <stdio.h>
int main() {
      int k;
      scanf("%d", &k);
      for(int i = 0, j = 1; i < k; ++i, ++j)
            printf("i: %d, j: %d\n", i, j);
      return 0;
}

4

i: 0, j: 1
i: 1, j: 2
i: 2, j: 3
i: 3, j: 4

초기화, 조건식, 증감문 중 어떤 것을 비워 놔도 그냥 없는 셈 치고 돌아갑니다.

#include <stdio.h>
int main() {
      int k;
      scanf("%d", &k);
      for(int i = 0; i < k; )   //무한 루프입니다!
            printf("i: %d\n", i);
      return 0;
}

4

i: 0

i: 0

i: 0

i: 0

....

for 안에 for를 쓰고 싶다는 생각은 안해보셨나요?

#include <stdio.h>
int main() {
      for(int i = 1; i < 10; ++i) {
            for(int j = i; j < 9; ++j)
                  putchar(' ');
            for(int j = 0; j < (i * 2 - 1); ++j)
                  putchar('*');
            putchar('\n');
      }
      return 0;
}

        *
       ***
      *****
     *******
    *********
   ***********
  *************
 ***************
*****************

고정폭 폰트로 보면 완전히 대칭인 모습을 볼 수 있습니다.

for문에서 continue를 만나면 그 for문의 시작으로 가 증감문을 실행하고 조건식으로 평가합니다. break를 만나면 현재의 for문을 탈출합니다.

#include <stdio.h>
int main() {
      for(int i = 0; i < 10; ++i) {
            if(i == 3) continue;
            printf("i: %d\n", i);
            if(i == 7) break;
      }
      return 0;
}

i: 0
i: 1
i: 2
i: 4
i: 5
i: 6
i: 7

3continue 때문에, 7 이후는 break 때문에 더 이상 실행되지 않습니다. 다만, 여러 겹의 for로부터 탈출하려면 얄짤없이 goto를 사용해야 합니다.

for 문을 사용하면 배열의 원소들을 쉽게 넣고 꺼내 쓸 수 있습니다.

#include <stdio.h>
int main() {
      int arr[ 5 ] = { 0, };
      for(int i = 0; i < 5; ++i)
            arr[ i ] = 5 - i;
      for(int i = 0; i < 5; ++i)
            printf("arr[ %d ]: %d\n", i, arr[ i ]);
      return 0;
}

arr[ 0 ]: 5
arr[ 1 ]: 4
arr[ 2 ]: 3
arr[ 3 ]: 2
arr[ 4 ]: 1

그리고 잘 보면 i++보다는 ++i를 많이 사용하는 모습을 볼 수 있는데, 이는 ++i가 그냥 1만큼 증가시키는 반면 i++i를 복사한 후 원본 i1만큼 증가하는 방식으로 움직이기 때문입니다. 즉, ++i가 빠릅니다. 물론 컴파일러에 따라 자동으로 ++i로 바꿔주기도 하지만, 혹시 모르니 ++i로 사용하는 것이 좋습니다.