[C언어]제어문

#언어
기본적으로 CPU가 알아듣는 프로그램의 형태는 기계어(Machine Language)다.
하지만 그 0과 1의 조합만으로 만들어지는 언어는 우리 인간이 알기가 너무 힘들고,
그래서 사람들은 인간과 기계의 중간 공용어인 프로그래밍 언어를 만들었다.

단순히 기계어를 간단한 영어단어로 바꾸어서 사람이 읽을 수 있게 한 것이 어셈블러(Assembler)이고,
흔히 코볼, 포트란 같은 요즘은 거의 쓰이지 않는 초창기의 프로그래밍 언어들을 저급언어라고 한다.
그리고 C, BASIC 등의 인간의 언어 개념에 가까운 언어들은 고급언어에 속한다.
(딱잡아 어디부터가 저급, 어디부터가 고급이라고 말하는 건 좀 우스운 애기고, 기계어에 가까울 수록 저급이고, 인간의 언어에 가까울 수록 고급이다.)
행렬(Matrix)구조를 기본 데이터구조로 삼는 MATLAB과 같은 언어는 더더욱 고급언어에 속할 것이다.

당연한 말이지만,
저급언어일수록 인간이 이해하고 사용하기가 힘들어 개발의 효율이 떨어지고,
고급언어일수록 기계의 동작을 효율적으로 제어할 수 있도록 번역(컴파일)하기가 힘들어져서, 동작의 효율이 떨어진다.

C언어가 그렇게나 오랜 세월동안 많이 쓰이고, C++이나 JAVA등의 닮은꼴들이 나오는 것은,
'수학적인 표현방식'을 사용함으로서 수학에 익숙한 인간이면 쉽게 접근할 수 있으면서,
기계어로 번역하기가 다른 고급언어에 비해 상대적으로 쉽기 때문에 
표현하고자하는 바가 CPU에게로 잘 전달되는 편이기 때문이다. 


#제어
그러한 언어들 중에서도 가장 기계어에 가까운 어셈블러를 공부해보면,
CPU의 제어장치가 갖는 명령어에는 앞서 다루었던 연산자들(산술,비트,논리 연산 등)에 의하여 연산장치를 제어하는 것 외에,
프로그램의 흐름 자체를 제어하는 명령어들이 존재하는 것을 알 수 있다.

일반적으로 점프(JMP)나 브렌치(BR) 명령군이 하는 이 역할은,
바로 프로그램이 있는 메모리 공간에서 다음 번에 어디에 있는 명령을 읽어올 것인가를 제어하는 것이다.

실행될 차례가 되었을 때, 혹은 어떤 조건이 갖추어졌을 떄 실행되는 이 명령들은,
프로그램이 원하는 조건에서 특정한 일을 수행하거나 원하는 동작을 반복할 수 있게 해주는 역할을 한다.

이것은 어떻게 보면 프로세서가 단순한 계산기와 다를 수 있게 되는 가장 핵심적인 기능이며,
C언어에서는 제어문을 통하여 그러한 기능들을 사용할 수 있게 되어있다.

앞서 다룬 연산자가 수학에서의 사칙연산에 해당된다면,
이 제어문들은 ∑, ∏, ∇, ∮ 등과 같은 연산의 묶음표현들에 해당하는 것들이라고 생각할 수 있을 것이다.


#if
앞서 연산자에서 다루었던 ?: 연산자로도 구현할 수 있지만, 기본적으로 if문을 통해 조건문을 만들 수 있다.
if()문은 괄호안의 연산결과가 0이면 다음의 내용을 실행하지 않고, 그렇지 않으면 실행하도록 동작한다.

많은 사람들이 if문 안에는 논리연산(<,>,==.!= 등)이 들어가야 할 것 처럼 생각하지만,
사실 반드시 그래야하는 것은 아닌 것이다.

if(a%5)
{
    //aaa
}

위 문장은 ()안의 연산결과가 0이면(a가 5의 배수이면) 동작되지 않고, 그 외의 값이면 동작된다.
if(a%5==0) 로 쓰면 더 와닿는 코드이지 않은가 싶을지도 모르지만, 이럴 경우 컴파일된 기계어는 a를 5로 나누는 명령, 0과 비교하는 명령을 한번 수행한 뒤 그 결과가 0인지 다시 한번 확인하게 된다. 같은 일을 두번 반복하게 되는 것이다.

#switch
switch문은 인자의 상태에 따라 다른 동작을 할 수 있는 조건문이다.
이는 if-else를 이용해서 완전히 같은 동작을 만들 수도 있지만, 어디까지나 코드의 분석을 용이하게 만들기 위해서 사용하는 것이라고 생각하면 된다.

switch(a)
{
case 0: //aa
    break;
case 1: //bb
    break;
default: //cc
}




if(a==0) //aa
else (a==1) //bb
else //cc


와 동일하다.
간혹 한 조건에서 수행되는 내용이 다른 조건에서의 수행내용에 포함될 때, switch문을 활용하여 break를 쓰지 않고 사용하면 if문을 쓰는 것보다 코드를 줄일 수 있다.
즉,

switch(a)
{
    case 0: //aa
    case 1: //bb
            break;
}




if(a==0)
{
    //aa
    //bb
}
else if(a==1)
{
    //bb
}


혹은
if(a==0) //aa
if(a==0 || a==1) //bb


와 같다.


#while
while()문은 괄호안의 내용이 0이 아닐 때 내용을 실행한다는 점에서 if문과도 같지만,
내용을 실해한 뒤 다시 한번 조건을 확인하고 조건이 일치되면 다시 내용을 실행하여 반복제어를 하게 된다.

~~할 때 까지 반복한다는 의미인 만큼,
어떤 조건이 일치될 때 까지 같은 일을 반복하거나 외부로부터의 입력을 기다릴 때 잘 쓰인다.

while(1) 의 형식으로 괄호안에 0이 아닌 상수를 넣어버리면 무한반복을 할 수 있다


#for
for문은 초기화,조건식,반복실행내용을 괄호안에 집어넣어서 코드를 줄일 수 있는 명령으로서,

for(i=0;i<10;i++)
{
    //aaa
}



i=0;
while(i<10)
{
    //aaa
    i++;
}


와 완전히 일치한다.
흔히 for문의 내부에는 위((i=0;i<10;i++))와 같이 특정 상수에 대한 조건으로 ++이나 --를 반복항에 넣어 사용할 경우가 많은데,
이는 특정횟수만큼 내용을 반복할 때 사용되는 방식일 뿐이다.
초기,조건,반복항은 서로 전혀 다른 동작을 의미해도 상관이 없다.

for(a=1,b=0; b!=0xff ; b+=a<<=1)



#do-while
do-while문은 내부의 내용을 한번 실행시킨 뒤 while문의 조건문을 확인하고 다시 실행할 것인가 여부를 결정하는 것으로서,
어떤 계산의 결과가 조건에 일치하는가 여부로 반복을 결정해야할 때 쓰인다.

do
{
    //aaa
}while(...);




//aaa
while(...)
{
    //aaa
}


와 완전히 일치한다.
두번째 방법은 내용(//aaa)이 두번 반복되어야하기 때문에,
코드가 길어져 읽기 힘들어지고 관리가 어려워 한쪽만 수정하거나 했을 때 버그가 생기기 쉬우므로, do-while을 쓰는 게 낫다.


#goto
잊혀진 비운의(?) 명령어인 goto문.
for, while, if문 만으로도 충분히 원하는 프로그램을 짤 수 있고,
여러 함수들 사이를 호출하고 리턴되며 돌아다니는 복잡한 프로그램에서
index관리가 제대로 이루어지지 않으면 프로그램이 꼬이게 되는 경우가 생길 수 있기 때문에 거의 쓰이지 않는다.

하지만 이런 것도 있다는 것을 기억해두면, 정말 어쩌다 가끔 정도는 유용하게 쓸 수도 있다.

예: a가 0이면 aa,cc를, 1이면 bb,cc를 실행
switch(a)
{
    case 0: //aa 
        goto cc;
    case 1: //bb
cc:    //cc
        break;
}