제어문

old/C Grammer 2010. 11. 15. 12:06

1) if문

조건문이란 주어진 조건에 따라 명령의 실행 여부를 결정하는 문장이다. 프로그램이란 항상 동일한 결과만 출력하는 것이 아니라 다양한 상황을 판단하여 다르게 동작하기도 한다. 이런 판단의 상황은 우리의 실생활에도 흔히 만나게 되는데 다음은 어느 백수의 일일 생활 순서도이다.

순서도에서 사각형은 동작을 나타내며 마름모는 조건 판단을 나타낸다. 마름모 안에 있는 문장이 조건인데 조건에 따라 특정 동작을 할 것인지 말것인지를 결정할 수 있다. 프로그램도 이와 마찬가지로 여러 가지 조건에 따라 상황을 판단하여 명령의 수행 여부를 결정한다. C언어의 조건문은 키워드 if를 사용하며 다음과 같은 형식을 가진다.

if (조건) 명령;

괄호안에 조건을 쓰고 이 조건이 만족할 때 실행할 명령을 괄호 뒤에 작성한다. 괄호는 조건과 명령문을 구분하기 위해 존재하며 생략할 수 없다. 베이직 언어의 조건문은 "if 조건 then 명령"으로 조건과 명령 사이에 키워드 then이 있는데 괄호나 then이나 모두 어디까지가 조건이고 어디부터 명령인지를 구분하는 역할을 한다. 조건은 주로 변수의 값을 비교하는 연산식인데 이때 다음과 같은 비교 연산자가 사용된다.

연산자

조건

==

좌변과 우변이 같다.

!=

좌변과 우변이 다르다.

>

좌변이 우변보다 크다.

<

좌변이 우변보다 작다

>=

좌변이 우변보다 크거나 같다.

<=

좌변이 우변보다 작거나 같다.

 수학에서 쓰는 등호, 부등호와 거의 유사하다. 단, 같다라는 조건은 =를 쓰지 않고 = 기호를 두 번 써서 ==로 표현한다. 그리고 다르다는 표현은 !=이라는 점을 주의하도록 하자. 부등 비교 연산자는 수학에서 ,≥로 표기하지만 이런 문자가 키보드에 없기 때문에 >=, <=로 표기하는데 =>, =<가 아님도 주의하도록 하자. "크거나 같다"라고 하지 "같거나 크다"라고는 하지 않으므로 자연어의 순서에 맞게 부등호가 먼저 온다고 외워 두면 헷갈리지 않을 것이다. 다음은 비교 연산자를 사용한 조건문의 예이다.

if (i == 5)           // i가 5이면
if (i != 5)            // i가 5가 아니면
if (i > 5)            // i가 5보다 크면

블록 안에 하나의 명령만 들어 있지만 { } 괄호로 묶었으므로 형태상 복문이다. 이 조건에 포함되는 다른 명령이 추가될 경우 { } 괄호안에 명령만 써 넣으면 된다. 명령을 미리 { }안에 작성해 놓으면 명령이 추가될 때 실수를 줄일 수 있고 소스를 읽기도 편해진다. 미리 { } 괄호를 싸 두지 않으면 명령만 추가하고 { } 괄호로 묶는 것을 깜박 잊어 버리는 경우가 많다. 아예 if 문의 기본 형식을 다음과 같이 암기하고 있는 것이 좋다.

if (조건) { 명령들 }

if 문의 기본 형식에 원래부터 { }가 포함된 것으로 생각하는 것이 바람직하며 if문을 입력할 때도 조건 다음에 아예 { }를 먼저 입력해 놓고 이 안에 명령을 작성하는 습관을 들여야 한다. 설사 if 조건에 걸리는 명령이 하나밖에 없어 { } 괄호가 불필요하더라도 이 괄호 때문에 프로그램이 더 커지거나 느려지거나 하지는 않는다. 블록은 하나의 문장으로 취급되므로 문장이 들어갈 수 있는 위치라면 블록도 언제나 들어갈 수 있다.

다음은 if문을 한 단계 더 확장해 보자.

if (조건) 명령1; else 명령2;

기본 if문은 조건이 만족할 때 특정 명령을 실행할 것인가 아닌가만 지정하는데 비해 else 문은 조건이 만족되지 않을 때의 동작까지도 같이 지정한다. else는 말 그대로 "그 외에"라는 뜻이며 조건이 만족되지 않을 때 실행할 명령을 지정한다. else가 있는 if문은 괄호안의 조건을 평가해 보고 이 조건이 참이면 명령1을 실행하고 거짓이면 명령2를 실행한다.

if (조건1) 명령1; else if (조건2) 명령2; else 명령3;

if (조건1) 명령1; else { if (조건2) 명령2; else 명령3 };


2) for 반복문

반복문은 비슷한 명령들을 여러 번 실행하는 제어 구조이다. 컴퓨터가 처리하는 데이터가 원래 반복적인 성격을 가지고 있기 때문에 반복문은 아주 많이 사용된다. 사실 컴퓨터가 제일 잘 하는 일이 아무 생각없이 주어진 명령을 계속 반복해 대는 것이다. 생각이 없다보니 속도도 빠르고 같은 일을 계속 시켜도 불평이 없다.

1번 학생부터 60번 학생까지 총점과 평균을 구하는 프로그램을 작성해야 한다면 똑같은 과정을 60번 반복해야 한다. 각 계산에서 달라지는 것은 학생의 번호뿐이며 총점을 구하는 방법이나 평균을 구하는 공식이 달라지는 것은 아니다. 이럴 때는 똑같은 처리를 60번 나열하는 것보다 한 번만 작성해 놓고 이 처리를 60번 반복하도록 하는 것이 훨씬 더 간단하다.

프로그램에서 이렇게 반복되는 부분을 루프(loop)라고 한다. 루프를 구성하는 방법에는 여러 가지가 있는데 C언어의 기본적인 반복문은 for문이다. for문의 형식은 다음과 같다.

for (초기식;조건식;증감식) 명령;

초기식 : 반복문은 보통 특정 변수가 일정한 범위에 있는 동안에 실행된다. 이때 반복문을 통제하는 변수를 제어 변수라고 한다. 초기식은 제어 변수의 초기값을 지정하며 루프가 시작될 때 한 번만 수행된다. i=0 이나 count=3 같은 대입문의 형식을 가지는 것이 보통이다.

조건식 : 반복문이 언제까지 실행될 것인가를 지정하며 이 조건이 참인동안 계속 루프를 돈다. 루프 실행을 계속할 계속 조건(탈출 조건이 아니라)이므로 조건이 거짓이 되면 루프를 탈출한다. 조건을 나타내므로 i < 10 또는 count < 100와 같은 제어 변수에 대한 비교 연산문이 온다. 조건문은 루프가 실행될 때마다 계속 평가된다.

증감식 : 한 번 루프를 돌 때 제어 변수를 얼마나 증감시킬 것인가를 지정한다. i=i+1 같이 제어 변수의 값을 변화시키는 연산문이 온다. 루프가 한 번 실행될 때 증감식도 한 번 실행된다.

명령 : 반복 실행될 명령이다. 하나의 명령이 올 수도 있고 { } 로 둘러싸인 복문이 올 수도 있는데 반복적인 처리는 보통 복문인 경우가 많다. 설사 루프에 포함된 명령이 하나뿐이더라도 실수 방지와 확장 편의성을 위해 가급적이면 { } 괄호를 싸 복문을 구성하는 것이 좋다.

for (i=1;i<=100;i=i+2)               // 1~100사이의 모든 홀수에 대해 반복
for (i=100;i>0;i=i-1)                 // 100~1까지 1씩 감소하며 역순으로 반복
for (f=0.1;f<=10.0;f=f+0.1)              // 0.1~10.0까지 0.1씩 증가하며 반복

(무한루프)

무한 루프란 반복 횟수가 미리 정해져 있지 않고 무한히 반복되는 루프이다. 제어 변수를 사용하는 루프는 제어 변수가 일정한 범위에 있을 때만 반복하므로 실행 회수가 미리 정해져 있는데 비해 무한 루프는 실행 회수를 미리 알 수 없다. 무한 루프를 만드는 방법은 아주 간단하다.

for (;;) {
           
명령;
}
 

조건식을 명시하지 않으면 이 조건은 항상 참으로 평가되기 때문에 루프가 끝나지 않게 된다. 그렇다면 무한 루프는 정말 무한히 반복되는가 하면 그렇지는 않다. 만약 정말로 무한히 반복된다면 루프 바깥의 코드가 실행될 수 없으므로 시스템 다운 상태가 되고 말 것이다. 무한 루프의 정확한 정의는 반복 회수가 가변적인 루프를 의미한다.

루프 자체에는 종료 조건이 포함되어 있지 않으며 명령을 실행하다가 일정한 조건이 되면 루프를 탈출한다. 즉 무한 루프란 형식상 무한히 반복되도록 해 놓고 루프 내부에서 끝낼 시점을 결정하도록 하는 루프이다. 그래서 무한 루프의 명령 블록에는 루프 탈출 처리가 반드시 포함되어 있어야 한다. 루프를 탈출할 때는 break문을 사용하는데 break는 조건식을 무시하고 강제로 루프를 종료하는 명령이다. 무한 루프의 일반적인 형태는 다음과 같다.

for (;;) {
     명령;
     if (탈출조건)
          break;
}

(다중루프)

다중 루프란 두 개 이상의 루프가 겹쳐 있는 제어 구조이다. 루프 안에는 반복의 대상이 되는 명령이 들어가는데 이 명령이 또 루프라면 이중 루프가 된다. 어떤 명령을 반복하는 동작을 또 반복하는 것이다. 그만큼 반복이란 흔한 동작이다. 다중 루프의 전형적인 예인 구구단 프로그램을 만들어 보자.

 다중 루프란 루프가 중첩(Nesting)되어 있는 것이다. 즉, 루프안에 루프가 완전하게 포함되어 있을 때 이를 다중 루프라 한다. 단순히 루프가 계속 이어진다고 해서 다중 루프가 아니다. 다음 예를 보자.

 

for (i=...) {
}
for (j=...) {
}

i루프와 j루프가 있지만 j가 i에 포함되어 있지 않고 i루프 바깥에 있다. 그래서 i루프가 완전히 종료되면 j루프가 시작된다. 이것은 단순 루프가 두 개 있는 것이지 다중 루프가 아니다.

for (i=...) {
    for (j=...) {
    }
}


3) while 반복문

while문은 for 문과 유사한 반복문이되 성격이 조금 다르다. 기본 형식은 다음과 같으며 키워드 while을 사용한다는 것 외에는 if문과 동일하다. if문은 딱 한 번만 조건을 판단하여 명령의 실행 여부를 결정하는데 비해 while문은 조건이 만족하는동안 명령을 계속 실행한다는 점이 다르다.

while (조건) 명령;

명령 자리에는 물론 여러 개의 명령을 묶어 놓은 복문이 올 수 있으므로 while (조건) {명령들} 이라고 외워두는 것도 좋다. while의 영어 뜻 그대로 조건이 참인 "동안" 명령을 계속 반복한다. 초기식이나 증감식 같은 것은 따로 없으므로 명령 블록에서 루프를 끝낼 수 있도록 해야 한다.

while문으로도 while (TRUE) 명령; 형식으로 무한 루프를 만들 수 있다. 조건이 TRUE로 고정되어 있으므로 while의 조건은 항상 참이 되어 명령을 무한히 반복할 것이다. 물론 정상적인 코드가 되기 위해서는 명령 블록 내에 일정한 조건이 되면 이 루프를 탈출(break)하는 문장이 포함되어 있어야 한다. 다음은 while문의 변형인 do while 문을 보자. 기본 형식은 다음과 같으며 do와 while이 짝을 이루어 사용된다. do만 있고 뒤에 while이 없으면 에러로 처리된다.

do 명령; while (조건);

 do 다음의 명령을 while의 조건이 만족하는 동안 반복적으로 실행하는데 파스칼의 repeat until 제어문과 동일하다. 명령은 보통 복문이 오므로 { } 괄호를 싸 주어야 한다.

for문 : 가장 큰 특징은 제어 변수를 사용한다는 점이다. 루프를 통제하는 변수를 선언하고 이 변수가 일정한 범위의 값을 가지는 동안 명령을 계속 반복한다. 그래서 통상 for문은 반복 횟수가 이미 정해져 있고 루프 중간에서 탈출하는 경우가 별로 없다. 물론 break문으로 강제로 탈출할 수도 있지만 일반적으로 반복 횟수가 정해져 있다. 그래서 for문은 1~100까지, 1번 학생~60번 학생까지의 경우처럼 미리 정해진 횟수만큼 반복할 때 가장 편리하다. 또한 문장안에 초기식, 조건식, 증감식이 포함되어 있어서 루프의 선두만 봐도 변수의 변화를 쉽게 파악하고 변경할 수 있다.

while문 : 루프를 계속할 조건만 있고 초기식이나 증감식이 없다. 아예 제어 변수라는 개념이 없으며 루프 내부에서 조건식의 진위 여부를 변경해야 한다. 그래서 while문은 반복 횟수가 가변적이다. 사용자의 입력이나 네트워크의 변화, 특정 신호의 입력 등 언제 발생할지 모르는 조건에 대해 반복할 때는 while문을 쓰는 것이 적합하다.

do~while : while문과 마찬가지로 제어 변수가 없고 반복 횟수가 가변적이지만 조건을 점검하는 시기가 다르다. while문은 루프로 들어가기 전에 조건을 점검하지만 do~while문은 일단 명령을 실행한 후 루프 계속 여부를 점검한다. 그래서 while문은 조건에 따라 한 번도 실행되지 않을 수도 있지만 do~while문은 최소한 한 번은 실행된다는 차이점이 있다. 요약하자면 while문은 선평가 후실행문이며 do~while문은 선실행 후평가문이다.

 

세가지 반복문은 상호 대체성이 있어서 for문 대신 while문을 쓸 수도 있고 while문 대신 do~while을 쓰는 것도 가능하다. 다음은 for문을 동일한 while문으로 변환하는 공식이다.

for (초기식;조건식;증감식) {

     명령;

}

초기식;

while(조건식) {

     명령;

     증감식;

}

 초기식을 먼저 실행하고 루프로 진입하며 매 명령을 실행할 때마다 증감식을 실행하면 while문으로도 for문과 똑같은 구조를 만들 수 있다. 물론 완전히 같지는 않아서 루프 내부에서 continue명령을 사용할 때의 효과가 약간 달라진다. 반대로 while (조건) 명령;도 for (;조건;) 명령; 형식으로 변환할 수 있다. 제어문에 따른 실행 속도나 코드의 크기는 거의 차이가 없으므로 실행 속도는 제어문을 선택하는 기준이 아니다.

하지만 어느 쪽이 더 효율적이고 코드의 가독성이 높은지, 부작용은 없는지를 비교해 보면 세가지 반복문 중 가장 적절한 것이 있을 것이다. 세가지 제어 구조의 특징을 잘 파악해야 상황에 가장 적절한 반복문을 선택할 수 있다. 1~100까지 숫자의 합계를 구하는 루프는 for문이 가장 적당하다. 반복 범위가 미리 정해져 있고 이 값이 루프내에서 사용되어야 하므로 제어 변수를 쓰는 것이 효율적이며 코드도 훨씬 더 짧고 명료하다.

정수를 입력받고 홀짝을 판별해서 메시지를 출력하는 일련의 코드를 do~while 루프로 감싸고 while의 조건문에 (i != 0)이라고 적으면 된다. 프롬프트를 출력하고 정수를 입력받은 후 홀짝 판별을 하는 코드 전체가 반복 단위임을 잘 파악해야 한다. 다음과 같이 반복 대상을 잘못 선택하면 엉터리로 동작하거나 차칫하면 무한 루프에 빠져 버릴 위험이 있다.

printf("정수를 입력하세요(끝낼 때는 0) : ");

do {

     scanf("%d",&i);

     if (i % 2 == 0) {

          printf("%d는 짝수입니다.\n",i);

     } else {

          printf("%d는 홀수입니다.\n",i);

     }

} while (i != 0);


printf("정수를 입력하세요(끝낼 때는 0) : ");

scanf("%d",&i);

do {

     if (i % 2 == 0) {

          printf("%d는 짝수입니다.\n",i);

     } else {

          printf("%d는 홀수입니다.\n",i);

     }

} while (i != 0);

 do~while문은 정수 하나를 입력받아 이 값의 홀짝을 판별한 후 i값을 평가해 보고 이 과정을 계속할 것인지 그만 둘 것인지를 결정한다. i가 0이 아니면 루프를 계속 실행하고 0이면 루프를 탈출한다. 따라서 0이 입력될 때까지 이 과정을 계속 반복할 것이다.


4) switch 문


다중 선택문이란 하나의 변수값을 평가하여 각 값에 대해 개별적인 처리를 지정할 수 있는 문장이다. 예를 들어 사용자가 입력한 숫자가 1일 때는 이렇게 하고, 2일 때는 저렇게 하고, 3일 때는 요렇게 하고 각각의 값에 대한 처리를 다르게 지정하고자 할 때 다중 선택문을 사용한다. 키워드 switch를 사용하며 기본 형식은 다음과 같다.

switch (변수) {

case 값1: break;

case 값2;

     // 값2에 대한 처리

     break;

}

switch문 다음의 괄호안에 평가할 변수를 적고 case문에 이 변수가 가질 수 있는 값과 이 값에 대한 처리 코드를 작성한다. case를 끝낼 때는 break문으로 switch 블록을 강제로 탈출해야 한다. case는 원하는만큼 얼마든지 작성할 수 있다. default는 case의 특별한 경우로 변수가 앞쪽 case에 있는 값 이외의 값을 가질 때의 처리를 지정한다.


5) 그 외 제어문

가) goto

goto문은 지정한 곳으로 무조건 점프하는 제어문이다. goto라는 말이 의미하듯이 조건없이 무조건 제어를 옮겨 버리기 때문에 사실 가장 사용하기 쉬운 제어문이다. goto로 제어를 옮길 지점은 레이블(label)이라는 것으로 표식을 단다. 블록의 끝만 제외하고 프로그램의 어느 곳에나 레이블을 배치해 놓고 goto 레이블명; 이라는 명령을 내리면 레이블 위치로 즉시 이동한다. 레이블보다 앞에서 뒤로 이동할 수도 있고 뒤에서 앞으로 이동할 수도 있되 단 함수 내에서만 이동할 수 있으며 다른 함수로는 점프할 수 없다.

레이블은 일종의 명칭이므로 명칭 규칙에만 맞으면 자유롭게 작성할 수 있다. 레이블 다음에 콜론(:)을 붙여 점프할 위치에 삽입해 놓기만 하면 된다. 다음이 goto문의 사용예이다.

here:
.....
.....
goto here;

점프하기를 원하는 곳에 here라는 이름으로 레이블을 붙여 놓고 goto here; 명령을 내리면 즉시 here 다음의 문장으로 이동한다. 다른 순환문과는 달리 복잡한 형식을 필요로 하지 않기 때문에 처음부터 제어 구조를 설계할 필요도 없고 언제든지 어느 곳으로나 제어를 옮길 수 있는 무척 간편한 명령이다.


나) break

break문은 이미 앞에서 여러 번 사용해 본 바 있다. 이 명령은 반복문이나 switch문 내에서 사용되며 루프를 강제로 벗어날 때 사용한다. for문이나 while문 내에서 break가 사용되면 조건식의 진위 여부에 상관없이 즉시 루프를 탈출하는데 우리말로 번역하자면 "당장 튀어 나와"라고 할 수 있다. 보통 if 조건문과 함께 사용되며 무한 루프에서 루프를 끝낼 조건이 되었을 때 break를 사용한다.

여러 개의 루프가 중첩되어 있는 다중 루프에서 break문이 사용되면 현재 루프 하나만 탈출한다. 다음은 이중 루프의 예이다.

for (i=...) {
     for (j=...) {
          break;
     }
}

다) continue

continue는 루프의 나머지 부분을 무시하고 조건 점검부로 점프하여 루프의 다음 값을 실행하도록 하는 명령이다. 루프를 돌던 중에 특정 조건에 대해서는 처리를 제외시키고자 할 때 이 명령을 사용한다. 루프의 조건을 다시 점검하도록 할 뿐이지 루프를 처음부터 다시 시작하는 것은 아니므로 제어 변수의 값은 그대로 유지되며 다음 증감문으로 이동한다.

for (i=...) {
     for (j=...) {
          continue;
     }
}

continue는 실전에서 그다지 자주 사용되지는 않으며 비교적 정밀한 제어 구조를 만들 때 가끔씩 사용된다.

출처: http://winapi.co.kr/

'old > C Grammer' 카테고리의 다른 글

기억 부류  (0) 2010.11.18
함수  (0) 2010.11.17
연산자  (0) 2010.11.16
변수  (0) 2010.11.12
현정누나 요약 C/C++  (0) 2010.08.17
Posted by jazzlife
,