C언어 제어문
목차
C의 제어문(flow control statement)은 특정 코드를 반복하거나 실행 여부를 판단 후 분기할 수 있습니다. 제어문은 크게 반복문(loop)과 분기문(branch)로 나눌 수 있습니다.
반복문은 말 그대로 반복하는 코드이고 분기문은 표현식의 반환값에 따라 실행할 코드가 선택됩니다. 컴퓨터 초창기에는 프로그램의 흐름을 제어하기 위해서 어셈블리어의 jump 명령어를 사용했습니다. C에도 goto 문으로 흔적이 남아있지만 절대 사용하지 말라고 하는 레가시 코드입니다만, 아무래도 프로그래머가 직접 반복문과 분기문을 만들다 보니 무한루프 등의 문제가 발생하기 쉽습니다.
while 문, for 문, if 문 등의 C의 제어문은 가독성이 뛰어납니다. C의 스타일에 영감을 받아서 만들어진 많은 언어들이 거의 비슷한 문법을 사용합니다. 자바의 경우 거의 똑같아 보이는데 이는 C언어를 배운 사람은 다른 프로그래밍 언어에 쉽게 적응할 수 있는 이유도 됩니다.
이 포스팅에서는 while 문을 학습함으로써 반복문의 기초를 알아보겠습니다.
while 루프
while 문, while 루프(loop)라고 합니다. 반복에 대하여 잠깐 설명을 하면 어떤 코드를 반복시키려면 조건을 줘야 합니다. 화면에 hello world 라는 단어를 반복해서 출력하는 예를 들어 봅시다. 코드 내용은 있으니까 몇 번 반복할지를 정해야 합니다. 무한정 반복하는 일도 가능하지만 그러면 hello world 를 출력하는게 끝이 없어서 강제 종료를 해야 할 것 입니다.
명확하게 반복 횟수를 주는 것이 루프의 기본입니다. hello world를 3번 반복하라는 명령을 말로 표현하면 다음과 같습니다.
- hello world를 출력한다
- 3번 출력했는가 확인한다 -> 아니다
- hello world를 출력한다
- 3번 출력했는가 확인한다 -> 아니다
- hello world를 출력한다
- 3번 출력했는가 확인한다 -> 맞다
- 코드를 종료한다
말로 표현하는 것을 더 논리적으로 다듬으면 의사코드를 만들 수 있습니다. 여기서는 평범한 언어로 표현해 봤습니다. 뭔가 불필요하게 반복되는 것 같은 생각이 들지만 컴퓨터에게는 그렇지 않습니다. 컴퓨터가 하는 중요한 일은 반복하는 일입니다. while 루프는 조건이 유효한 동안(while~) 반복문을 돌립니다.
반복은 유한루프와 무한루프 두가지가 있습니다. 유한루프는 반복문이 실행되는 회수가 유한개이고 무한루프는 무한으로 실행됩니다.
유한루프는 카운터를 사용해서 횟수를 정할 수도 있지만 while 루프에서는 조건으로 콘트롤 합니다. for문과 while 문은 반복을 한다는 점에서 같지만 방식에 큰 차이가 있습니다. for 문은 초기에 명확하게 조건이 주어질 때 가독성이 좋습니다. for(int i=0; i<100; i++) 는 for가 i 인덱스가 0부터 99까지 실행됩니다. 여기서 i는 자료형이 int 로 4바이트입니다. 인덱스의 한계가 곧 반복회수의 제한을 결정하는데 이 for 루프는 약 21억회를 반복할 수 있습니다. 개수를 더 늘릴려면 8바이트 정수형을 사용하거나 부동소수점 등 다른 방법을 사용해야 하는데 인덱스를 처리하는 시간도 고려해야 합니다.
한편 while 루프는 조건 검사에 의해서 횟수가 정해집니다. 예를 들어 running=1; while(running){ } 이렇게 하면 running 이란 변수가 0이 될 때까지 실행합니다. for 문은 변수 타입에 대한 제한이 있었지만 while문은 조건으로 따지기 때문에 4바이트 8바이트 숫자의 제한이 없습니다.
좀 더 생활적인 비유를 해볼까요? 공장에는 제품의 일일 생산량이 있습니다. 제품은 똑같은 과정을 반복해서 만들어지니까 하나의 루프라고 볼 수 있습니다. 오늘 생산계획이 300개라면 똑같은 과정(루프)을 300번 반복하면 됩니다. C언어라면 for(int i=0; i<300; i++) 로 표현할 수 있습니다. 그런데 이 공장이 실시간으로 주문을 받아서 만든다면 어떨까요? 주문 마감시간이 4시까지면 300개라는 특정 개수를 처음에 확정하지 않을 겁니다. for 문으로 못할 것은 아니지만 뭔가 딱 들어맞지 않는 것 같습니다. 그렇다면 조건 변수를 하나 만들어서 처리하면 좋을 것 같습니다. 공장이 주문을 받는 상태면 1을 마감을 한 상태면 0이라고 하는 변수 orderStatus 를 만든다면 while(orderStatus) 로 표현할 수 있습니다. 주문을 받고 있다면 while 문이 실행될 것이고 마감한 상태에서는 실행하지 않습니다. 이 방법의 장점은 인덱스 변수의 타입에 대한 제한이 사실상 없어집니다. while 문은 한번도 실행하지 않을 수도 있고 무한대에 가깝게 실행할 수도 있습니다.
무한에 가까운 반복문은 우리의 일상생활에도 흔히 사용됩니다. 예를 들면 윈도우는 무한루프를 돌고 있습니다. 사용자가 마우스를 움직일 때 마다 반복해서 위치를 추적하고 있습니다. 언제까지 도냐면 프로그램이 끝날 때 까지 입니다.
CLI(Command Line Interface)의 명령 프롬프트도 무한루프를 돌고 있습니다. 리눅스의 Bash Shell은 사용자가 명령어를 입력하는 것을 루프를 돌면서 대기하고 있습니다. 리눅스를 shutdown 시킬 때 까지 Shell이 열려있어서 루프를 돌며 대기하는 것 입니다.
남녀노소 즐겨하는 컴퓨터 게임도 게임루프(Game Loop)라는 무한루프를 돌고 있습니다. 게임은 다른 앱들과 마찬가지로 프로그램의 한 종류인데 사용자의 조작장치(마우스, 키보드, 조이스틱 등)에 보통의 사무 프로그램보다 민감하게 반응하도록 설계되어 있습니다.
인터넷 서비스를 제공하는 서버 프로그램도 무한 루프를 돌고 있습니다. TCP Port로 들어오는 요청을 실시간으로 처리합니다. 서버는 1년 365일간 루프를 돌고 있습니다. 인터넷이 24시간 작동하는 것은 서버가 24시간 돌아가기 때문입니다.
이런 프로그램을 만들 때 루프를 사용합니다. 앱의 특성에 따라 while 문을 직접 작성할 때도 있고 또는 루프를 사용하는 시스템 자원을 호출하는 경우도 있을 겁니다.
이제 while 문이 왜 필요한지는 충분히 이해할 것 입니다.
나머지는 간단한 예제를 통해서 사용법을 알아보는 것 입니다. while 문에 관한 좋은 예제는 인터넷에 검색하면 무수히 찾을 수 있으니 여기서는 간단히 보겠습니다.
다음 예제는 1부터 30까지 합을 차례로 출력합니다. 일단 while문을 횟수를 정해서 사용해봅니다. 그럴려면 while문 이전에 인덱스(카운터)를 설정하고 while문 내부에서 인덱스 값에 변화를 줘야 합니다. 아래는 인덱스 0부터 29까지 30회를 실행하며 1부터 30까지 합산합니다.
C언어를 시작하는 사람들은 인덱스가 0부터 시작하는 것에 익숙하지 않을 수 있습니다만, 일종의 convention 이기도 하고, 컴퓨터에서는 메모리 주소를 0부터 부여하고 바이트(byte)의 시작은 0이기 때문에 위치를 표현하는 인덱스가 0부터 시작하는 것에 익숙해질 필요가 있습니다.
#include<stdio.h> int main() { // while 루프 예제 1부터 30까지 합 int count=0; long sum=0; while(count<30) { count++; sum+=count; printf("%3ld, ", sum); if(count%5==0)printf("\n"); } printf("\n-> sum: %ld\n", sum); return 0; } [실행결과] 1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 66, 78, 91, 105, 120, 136, 153, 171, 190, 210, 231, 253, 276, 300, 325, 351, 378, 406, 435, 465, -> sum: 465
다음 예제는 사용자의 정수 입력이 끝날 때 까지 루프를 하는 코드입니다. 이를 위해 running 이란 변수를 사용합니다. scanf 함수는 0과 1을 리턴합니다. 저장이 성공적이면 1을 실패하면 0이나 EOF 등 다른 상수를 리턴합니다. 이것을 이용해서 while 문의 시작지점에 running==1 처럼 비교하면 숫자가 제대로 입력된 경우에만 while 문을 실행시킵니다. 즉 while 문에서 처리할 변수가 제대로 확보된 후에 문을 실행시킵니다.
함수의 매개변수 규칙과도 비슷한 부분이 있습니다. 매개변수가 정확히 확보가 안되면 함수를 호출할 수 없습니다. while 문에서는 조건이 제대로 갖춰지지 않으면 실행되지 않습니다. while 루프가 만들어내는 무한루프는 프로그램의 통제가 쉽습니다. 흐름 제어를 jump로 했던 어셈블리어를 생각하면 상당히 획기적인 방법입니다.
#include<stdio.h> int main() { long var1; long sum=0L; int running; printf("가산할 정수를 입력하시오(종료 q)\n"); running=scanf("%ld", &var1); printf("%d\n", running); while(running==1) // while 조건은 0이 되면 종료 { sum+=var1; printf("current sum: %ld\n", sum); printf("가산할 정수 입력하시오(종료 q)\n"); running=scanf("%ld", &var1); } printf("final amount : %ld\n", sum); return 0; } [실행결과] 가산할 정수를 입력하시오(종료 q) 7 1 current sum: 7 가산할 정수 입력하시오(종료 q) 5 current sum: 12 가산할 정수 입력하시오(종료 q) 13 current sum: 25 가산할 정수 입력하시오(종료 q) 12 current sum: 37 가산할 정수 입력하시오(종료 q) 56 current sum: 93 가산할 정수 입력하시오(종료 q) 42 current sum: 135 가산할 정수 입력하시오(종료 q) -30 current sum: 105 가산할 정수 입력하시오(종료 q) q final amount : 105
정리
이번 포스팅에서는 C언어 제어문과 while 문에 대해서 설명했습니다. while 문이 사용되는 분야나 의미에 대해 강조한 부분이 있는데 아직 C의 기초 단계에서는 while 문의 실용성을 잘 몰라서 의미가 없다거나 지루하게 생각할 수 있는데 중급 레벨 정도에 도달하게 되면 아주 능숙하게 사용하게 될 것 입니다.