첫번째 프로그램, Hello World

+/-

모든 프로그래밍 서적에서 맨 처음에 필수요소급으로 나오는 코드 짜기

가장 무식한 방법이긴 하지만 가장 좋은 방법은 역시 해보고 나서 그것이 무엇인지 고민해 보는 것 이라고 믿는다. 아래의 코드를 입력한후 컴파일을 해서 실행 결과를 보도록 하자[1]. 프로그램을 입력할 땐 가장 앞줄의 숫자는 입력해서는 안된다. 그 숫자들은 단순히 설명하기 쉽도록 하기 위해 문장의 줄 번호를 기록해 둔 것 뿐이다.

hello_world.c

#include<stdio.h>
int main(int argc, char * argv[])
{
    printf("Hello World!\n");
    return 0;
}

위 프로그램을 컴파일 하고 실행하면 화면에 Hello World! 라는 문장이 찍힌 것을 볼 수 있을 것이다. 환영 문구가 화면 구석에서 멍하니 당신을 바라보고 있다면, 이제 프로그래머가 된 것이다. Welcome to Hell :)

위 프로그램은 아주 단순하기는 하지만, C 프로그래밍을 하는데 있어 가장 기초적이고 꼭 필요한 정보를 거의 다 담고 있다. 4 번 줄에 해당하는 위치에 무언가 다른 내용이 포함될 수 있겠지만, 기본적인 구조는 항상 위의 코드를 따른다는 것을 기억해 두기 바란다.

1 번줄, 가장 위에 있는 문장 '#include<stdio.h>'전처리문이라 부르며, 어디에 있는 파일인지 모르지만 stdio.h라는 파일을 읽어서 그 위치에 넣어 달라는 의미를 갖는다. 이 stdio.h 라는 파일안에는 printf()라는 화면에 정보를 출력하는데 필요한 함수(function)를 쓰는 방법에 대한 정보가 기록 되어 있다. 물론, 그 외의 기초적인 입출력과 관계된 정보들이 들어있다[2].

2 번줄에서 main이라는 이름을 갖는 함수를 선언 하고 있다. C라는 언어는 함수라는 블럭으로 구성된 프로그램이라고 생각하면 된다. 자세한 이야기는 나중에 함수섹션에서 다시 다루도록 하고, 지금 기억해 둘 것은 'C로 만든 모든 프로그램에는 main 이라는 함수가 반드시 존재해야 한다'는 사실이다.

참고: 프로그램이 실행된다는 것은 운영체제(Operating System)가 보조기억장치 -- 하드디스크에서 프로그램 파일을 읽어 메모리에 올려놓고 프로그램이 실행되는데 필요한 준비작업을 마친 뒤 미리 약속된 진입함수(entrance function)를 호출하는 절차를 수행한다는 의미이다. 그렇기 때문에 모든 프로그램에는 반드시 진입 함수가 필요하다[3].

수학에서 함수란 '어떤 정보를 가지고 가공해서 결과를 내놓는 것'을 말한다는 것을 대부분의 독자들은 알 고 있으리라 믿는다. C 언어에서 말하는 함수 역시 동일하다. 함수에게 정보를 넘겨주고, 그 정보를 함수의 내부에서 처리해서 처리된 결과를 돌려주도록 되어 있는 구조이다.

함수에게 넘겨주는 정보는 함수의 이름 뒤에 괄호를 치고 정보의 종류와 이름을 나열하도록 되어 있다[4] 이렇게 함수에게 넘겨주는 정보를 매개변수(parameter)혹은 인수(argument)라고 부른다. 프로그램 코드에 보면 main 다음에 괄호치고 int argc와 char * argv라는 두 개의 정보를 표기한 것을 볼 수 있다. 이 두 인수의 의미는 숫자로 된 정보 하나와, 단어들을 함수의 입력으로 쓰겠다는 뜻이다. 프로그램에 지정되어 있는 두 인수 argc와 argv는 사용자가 프로그램을 실행시킬때 입력한 정보를 운영체제가 처리해서 두 인수에 넣어주게 되어 있다. 이와 관련된 내용은 프로그램 실행시 입력된 사용자 입력정보 처리방법에서 다루도록 하겠다.

중요사항:

main 함수의 매개변수는 생략할 수 있도록 되어 있다. 프로그램을 실행할 때 사용자가 어떤 정보를 직접 입력하지 않아도 되는 경우에는 이 매개 변수를 생략하는 경우도 많다.

입력된 정보를 처리해서 얻은 처리 결과는 5 번 줄에서 처럼 return이라는 명령어--키워드를 이용해서 결과를 내놓게 된다(보통은 '돌려보낸다' 혹은 '리턴 한다'는 표현을 쓴다). 이때 함수를 만드는 시점에서 함수를 이용해 만들어지는 결과가 어떤 종류인지를 미리 지정해 주어야 하는데 main 함수의 경우에는 보통 int로 정해져 있으며, 운영체제에게 프로그램이 실행된 결과를 알려주는 용도로 사용된다 [5]. 경우에 따라서는 프로그램이 실행된 결과가 없을 수 도 있는데, 그 경우에는 프로그램이 제대로 실행되고 끝났는지 여부를 나타내는 용도로 사용하기도 한다. main 함수의 리턴값이 프로그램이 제대로 실행되었는지를 나타내는 용도로 사용되는 경우, 프로그램이 정상적으로 실행되고 종료되면 0을 리턴하는 것이 일반적이며 운영체제에게 '이 프로그램은 기대했던 대로 실행되었다'라는 의미로 사용된다. 이경우 0이 아닌 값을 리턴하는 경우에는 미리 지정된 에러코드를 리턴하는 경우도 많다. 에러처리와 관련된 내용은 C 프로그래밍의 에러처리 항목에서 다루도록 하겠다.

함수를 만들려면 이 함수에 포함되는 내용이 어디서부터 어디까지 인지 표시를 해줄 필요가 있다. 위의 프로그램 에서는 보기 좋게 하기 위해서 줄도 바꾸고 들여쓰기도 한데다가, 프로그램 자체가 짧아 쉽게 main 프로그램의 시작과 끝을 알아볼 수 있다. 그러나 C 컴파일러는 스페이스나 엔터나 탭이나 모두 같은 스페이스로 간주한다. 다음의 프로그램은 위의 프로그램과 완전히 동일한 프로그램이다.

#include<stdio.h>
int main(int argc, char * argv[]){printf("Hello World!\n");return 0;}

한 때, 어쩌면 지금도 하고 있는지도 모르겠다. 원라인 프로그램 경진대회라는 것이 있었다. 프로그램을 단 한줄로 짜서 누구의 프로그램이 더 멋지게 동작하는지를 겨루던 시합 이었다. 실제로 한 줄의 길이는 255자로 간주했었기 때문에 255자 이내의 프로그램을 얼마나 잘 짜느냐 하는 시합이기도 했다. 어찌 되었든 프로그램을 한 줄로 줄이고 보니 main 이라는 함수의 시작과 끝을 무엇으로 표시했는지 분명하게 알 수 있게 되었다. main 이라는 함수에 포함된 모든 프로그램은 '{'와 '}' 사이에 들어있다. 실제로 '{'와 '}'는 영역을 표시하는 용도로 사용되며, 두 괄호 사이의 영역은 다른 영역과 구별된 고유의 영역이라는 뜻으로 사용된다. 두 대괄호의 용도에 대해서는 진행하면서 천천히 예를 들어 보일 수 있을 것 이라 생각하고, 지금은 함수의 시작과 끝을 표시하는 용도로 쓰인다는 정도만 기억해 두면 되겠다.

마지막으로 4 번 줄에 있는 내용을 함께 보도록 하자. printf() 라는 함수는 표준출력(standard output; 보통은 모니터를 말한다)으로 지정된 문자열을 출력하도록 하는 함수이다. 앞서 말했듯 stdio.h 라는 헤더파일 안에 printf() 함수를 사용하는데 필요한 정보들이 들어있다. 괄호안의 쌍 따옴표 안에 있는 문자열을 출력하도록 해주는 함수라는 것은 프로그램을 실행시켜본 사람이라면 쉽게 추측할 수 있을 것이다. 여기서 잠시 관심을 가져 주어야 할 것이 문자열의 제일 마지막에 있는 '\n'이다. 이 문자는 줄바꿈 문자로, 이 문자의 위치에서 출력 위치를 다음 줄로 옮기라는 의미라고 생각하면 된다. '\n'과 같이 키보드로는 입력할 수 없는 문자를 지정하기 위해 쓰는 문자를 확장문자(escape character)라 부른다.


변수의 선언과 사용

+/-

일단 아래의 코드를 입력하여 실행해 보자:

hello_variable.c

#include <stdio.h>
int main (int argc, char * argv[])
{
    int number;
    number = 300;
    printf ("The value in Number is %d.\n", number);
    return 0;
}

이번에는 '변수'라는 개념에 대해 설명해 보도록 하겠다. '변수'라는 것은 기억공간을 말한다. 컴퓨터 프로그램을 하다보면 여러 가지 정보를 저장해야 하는 경우가 있는데, 이 정보를 저장하는 장소가 바로 변수이다. 또한 변수에 들어있는 정보는 언제라도 그 값을 바꿀 수 있다. 다시 말해서 그 안에 들어있는 값이 변하기 때문에 '변수(variable)'라고 한다. 그에 반해서 변하지 않는 값을 '상수(constant)'라 한다. 예를 들어 말하자면, 위의 프로그램에서 4번 줄에서 선언된 number라는 것은 그 안에 어떤 값이 들어갈 수 있는 변수이고, 5번 줄의 등호 왼쪽에 있는 300이라는 값은 값이 변할 수 없는-당연하다-상수이다.

이제 앞에서 제시된 프로그램을 같이 점검해 보도록 하겠다. 먼저 1, 2, 3번 줄은 앞에서 설명한 내용과 동일한 내용이니 설명하지 않도록 하겠다.

4번 줄은 변수를 선언하는 문장이다. C 컴파일러에게 내가 이러이러한 변수를 쓸 테니까 준비하라는 뜻으로 생각하면 된다. 앞에 있는 int는 해당 변수에 저장될 내용이 정수(integer)라는 것을 나타낸다. 다른 대부분의 언어에서와 마찬가지로 C에서도 미리 지정된 형태의 데이터만을 변수에 저장해야 한다는 것을 기억해 두어야 한다. 다시 말해서 정수형으로 지정된 변수에는 정수 값만을 넣어야 한다. 그리고 뒤에 있는 number는 선언된 변수의 이름이다. 선언된 변수에 어떤 값을 넣거나, 변수 안에 있는 값을 읽고자 할 때 바로 이 변수의 이름을 가지고 일을 하게된다. 변수의 이름은 사용자 마음대로 정할 수 있는데, 몇 가지 규칙이 있다:

  1. 변수의 이름에 사용될 수 있는 문자는 영문자, 숫자, 언더스코어(_)이다.
  2. 변수의 이름이 숫자로 시작되어서는 안 된다.
  3. C는 대문자와 소문자를 구별하여 사용해야 한다. --> 이거 안지키다가 뭐가 에러가 났는지 원인을 몰라 헤매는 새끼들 존나 많다. 다행히 워닝메시지가 뜨면 양반이고.
  1. 변수의 이름은 어떤 것을 사용해도 되지만, 가능하면 의미 있는 이름을 사용하는 것이 좋다.
  2. 그 외에 변수의 이름을 정해줄 때, 간단한 정보를 포함하는 것도 프로그램을 작성하는데 도움을 받을 수 있을 것이다.
예를 들어 앞에서 선언한 변수의 이름이 number인데, 제일 앞에 'i'를 붙여 'iNumber'라는 형태로 써주면 해당 변수가 정수형 변수라는 것을 나타낼 수 있다. 이 규칙만 안다면 iNumber라는 변수 이름은 '숫자가 들어갈 정수형 변수'라는 의미를 지니고 있다는 것을 선언부를 보지 않고도 충분히 알 수 있다.[6]

5번 줄의 문장은 'number'라는 변수에 300이라는 값을 집어넣으라는 의미이다. 여기에서 등호는 두 개의 값이 같다는 뜻이 아니라, 등호의 왼쪽에 있는 값(일반적으로 lvalue라고 한다)을 등호의 오른쪽에 있는 변수(일반적으로 rvalue라고 한다)에 집어넣으라는 뜻이다. 이렇게 해줌으로서 number라는 변수에는 300이라는 데이터가 저장된다.

6번 줄의 문장은 이미 한번 언급했던 printf() 함수인데, 여기에서는 변수의 값을 화면에 인쇄하기 위해서 몇 가지가 더 추가되었다. 먼저, 인용부호 "와 "사이에 있는 문장은 화면에 그대로 출력된다는 사실을 이미 설명하였다. 그 다음 컴마(,)뒤에 number라는 변수를 볼 수 있을 것이다. 이 변수는 물론 4번 줄에서 선언한 변수에 5번 줄에서 300이라는 값을 집어넣은 바로 그 변수이다. 물론 printf() 함수 안에 들어가 있으니 그 변수의 값을 화면에 인쇄하게 될 것이다. 이때 컴마는 인쇄될 두 내용, 인용부호내의 내용과 변수의 값을 분리하기 위해서 사용된 것이다. 문제는 number라는 변수의 값이 과연 어느 위치에 인쇄가 될 것인가 하는 것인데, 인용부호 내의 문장을 보면 '%d'라는 내용을 볼 수 있을 것이다. 바로 이 위치에 number라는 변수의 값이 인쇄가 된다. 프로그램을 컴파일하고, 실행시켜 보면 어느 위치에 값이 인쇄가 되는지 확인할 수 있을 것이다.

C에서 사용할 수 있는 변수의 종류는 정수형 외에도 실수-C에서는 부동소수(floating point)라고 부른다-, 문자 하나를 담을 수 있는 문자형이 존재한다. 실수형 변수를 선언하고자 한다면 float라는 키워드를 사용하며, 문자형 변수를 선언하고자 한다면 char라는 키워드를 사용한다. 그 외에도 변수에 저장할 수 있는 값의 크기에 따라 몇 가지 다른 형태도 있지만, 기본적으로 세 가지 형태만 알고 있으면, 프로그램을 작성하는데 별 문제는 없을 것이다. 부동소수형(실수형)변수를 선언할 때는 다음과 같이 한다.

float float_data;

물론 위와 같이 선언하면 부동소수형 변수 float_data를 사용할 수 있도록 될 것이다. 그 다음 문자형 변수를 선언할 때에는

char char_data;

로 선언을 해주면 문자하나--물론 한글이 아닌 영문자 하나를 저장할 수 있는 변수가 선언된다. 원칙적으로 정수형 변수에 문자가, 부동소수형 변수에 정수 값을 사용하면 안되므로 적당한 변수의 형을 선언하는 것이 중요하다. float형이나, char형 변수를 printf 함수를 이용해서 인쇄하기 위해서는 한가지 내용을 더 설명해야 한다. 이전에 정수형 변수를 인쇄할 때 인쇄되는 위치를 나타내기 위해서 '%d'를 사용했던 것을 기억할 것이다. 이때 d는 deciaml의 약자로 정수를 10진수로 표기할 것이라는 뜻이다. 그렇기 때문에 %d를 가지고 부동소수를 인쇄하거나, 문자를 인쇄하면 전혀 예상하지 못한 결과를 얻게 될 것이다. 다시 말해서 부동소수 값이나 문자형 변수의 값을 인쇄할 때 %d를 사용해서는 안 된다는 말이다. 부동 소수형 변수의 값을 인쇄할 위치를 정할 때에는 %d대신에 %f를 사용해야 하고, 문자형 변수의 값을 인쇄할 위치를 정할 때에는 %c를 사용하면 된다.

변수의 종류를 자세히 알고자 한다면 '자료형'을 참조하면 되고, 각 자료형의 인쇄되는 위치를 정하는 기호를 알기 원한다면 printf()함수를 참조하면 될 것이다.

자, 그러면 프로그램을 하나 작성해 보도록 하자, 부동소수 '3.141592'를 인쇄하는 프로그램을 작성해보도록 한다. 이 프로그램 역시 지금까지 한 이야기를 이해했다면 아주 간단하게 작성할 수 있을 것이다. 3.141592를 인쇄하는 프로그램을 완성했다면, 다시 문자 'x'를 인쇄하는 프로그램을 작성해 보도록 하자. 참고로 문자형 변수에 문자를 집어넣으려고 하는 경우에는 다음과 같이 한다.

one_char = 'x';

이때 '가 아닌 "를 사용해서는 절대 안 된다. C에서는 '와 "는 전혀 다른 의미를 가지고 있기 때문에 '가 아닌 "를 사용하면 전혀 예상치 않은 결과를 얻게 될 것이다.

출력할 때 조건을 다는 방법

+/-

언제나처럼 다음의 프로그램을 입력해 보자.

#include <stdio.h>
int main (int argc, char * argv[])
{
    int age;
    printf ("몇살이죠 ? ");
    scanf ("%d", &age);
    if (age < 19) 
    {
        printf ("이 영화는 미성년자 관람불가 입니다.\n");
    };
    printf ("%d 살이란 말이죠... 매진되었는데요 ^^;\n", age);
    return 0;
};

프로그램이 지난번보다 약간 더 길어졌다. 그러나 7번 줄을 제외하고는 모두 알고 있으리라 생각한다. 그리고 7번 줄의 문장 역시 눈치가 빠르다면 쉽게 이해할 수 있을 것이라 생각한다. 7번 문장의 의미는 변수 age의 값이 19보다 작으면 8번 줄과 10번 줄 사이의 문장을 실행하라는 말이다. 8번 줄의 '{'와 10번 줄의 '}'는 if문장의 조건이 맞는 경우 처리해야할 내용의 시작과 끝을 표시한다. 7번 줄에서 10번 줄의 문장을 우리가 익숙한 말로 바꾼다면 다음과 같을 것이다.

"만일 나이가 19살이 안되면 '이 영화는 미성년자 관람불가 입니다'라고 화면에 출력한다"

그럼 프로그램에 몇 줄 더 추가해서 나이가 90살이 넘으면 "와 ! 정말 이세요 ?"라는 문장을 화면에 인쇄하는 프로그램을 만들어 보라.

비교할 때 쓸 수 있는 것은 '<'외에도 '>', '==', '>=', '<=' 그리고 '!=' 가 있다. '=='는 같다 라는 것을 의미하는데 그냥 '='를 쓰지 않는 것은 앞에서 이야기했던 대입문과 헷갈리지 않도록 하기 위한 것이다. 그런데 문제는 이게 잘 헷갈린다는 것이다. 어쨌든 좀 이상하다고 느낀다면 혹시 잘못 쓰지 않았나 확인해 보는 것도 좋을 것이다. '>=' 크거나 같다, '<='는 작거나 같다, 그리고 '!='는 다르다 라는 의미를 갖는다. C에서 '!'의 의미는 'not'의 의미를 갖는다는 것을 알아두면 이해하기 쉬울 것으로 생각한다.

이번에는 '아니면'이라는 조건을 한번 생각해보자. 위의 프로그램을 수정해서 다음과 같은 일을 수행하는 프로그램을 함께 만들어보도록 하자.

"만일 나이가 19살이 안되면 '이 영화는 미성년자 관람불가 입니다'라고 화면에 출력하고, 아니면 '표 값은 6000원 입니다.'이라고 출력한다"

C에서 '아니면'이라는 의미를 갖는 문장은 'else'이다. 결국 위의 문장을 약간 바꾼다면 다음과 같이 쓸 수 있겠다.

"if 나이가 19살이 안되면 '이 영화는 미성년자 관람불가 입니다'라고 화면에 출력하고, else '표 값은 6000원 입니다.'이라고 출력한다"

충분히 프로그램을 작성할 수 있겠지만, 이해를 돕기 위해서 프로그램을 한번 작성해 보겠다. 앞에서 설명했던 프로그램의 내용을 약간 수정하는 정도로 충분하다.

#include <stdio.h>
int main (int argc, char * argv[])
{
    int age;
    printf ("몇살이죠 ? ");
    scanf ("%d", &age);
    if (age < 19) 
    {
        printf ("이 영화는 미성년자 관람불가 입니다.\n");
    }
    else 
    {
        printf ("표 값은 6000원 입니다\n");
    };
    return 0;
};

10번 줄까지는 앞에서 설명한 프로그램과 동일해 보인다. 그러나 10번 줄을 자세히 보면 제일 마지막에 있던 세미콜론(';' - semicolon)이 없어진 것을 확인할 수 있을 것이다. 이미 설명했듯이 세미콜론의 용도는 문장이 여기에서 끝났다는 것을 의미한다. 하지만 위의 프로그램에서 'if' 문은 10번 문장에서 끝나는 것이 아니다. 뒤에 '아니면'이라는 문장이 따르고 있다. 그러므로 여기에 세미콜론을 찍으면 'if'가 없는 'else'가 있다는 식의 에러 메시지를 볼 수 있을 것이다. 실수하기 쉬운 부분인데 'else'문장이 따르는 경우에는 절대 세미콜론을 찍어서는 안 된다.

같은 일의 반복을 쉽게 해보자

+/-

먼저 다음의 프로그램을 입력해서 실행을 해보자, 그럼 어떤 일을 하는 프로그램인지 알 수 있게 될 것이다.

#include <stdio.h>
int main (int argc, char * argv[])
{
    int counter;
    for (counter = 1; counter <= 10; counter++) {
        printf ("헉 !\n");
    }
    printf ("휴 ~\n");
    return 0;
};

이번에도 낮 선 문장은 딱 한 줄 뿐 일 것이다. 하지만 상당히 많은 내용이 포함되어 있고, 헷갈리기 쉬운 내용이기도 하니 긴장이 필요하다. 프로그램을 실행시키면 달리기라도 하는지 헉헉거리다가, 휴 하고 길게 숨을 내쉬는 컴퓨터를 보게 될 것이다. 그런데 틀림없이 "헉" 이라는 내용을 인쇄하는 문장은 5번 문장 하나뿐임에도 10번이나 인쇄가 된다. 물론 이유는 4번 줄의 명령에 있다. 그럼 함께 그 비밀(?)을 풀어보자.

for 라는 문장은 반복을 하고자 하는 경우에 사용되는 문장이다. 이 for라는 문장은 세 부분의 보조 문장을 갖게 되는데, 각각은 ';'으로 구별하도록 되어있다.

첫 번째 부분은 반복하는데 횟수를 세게될 변수를 초기화 하는데 사용되고, 두 번째는 반복문을 계속할 조건을 쓴다. 그리고 마지막으로 횟수를 세게될 변수의 변화식을 지정하게 된다. 그럼 4번 문장의 의미를 하나 하나 따져 보도록 하자.

제일 앞의 'counter = 1'의 의미는 카운터로 사용할 변수 counter의 값을 1로 초기화 하는 것이다. 다음의 'counter <= 10'은 반복문을 반복할 조건으로 counter의 값의 10보다 작거나 같은 동안 반복하게 된다는 것을 의미한다. 마지막에 있는 'counter++'의 의미는 counter에 들어있는 값을 하나 증가시킨다는 의미이다. 예컨대 counter의 값이 1 이었다면, 이 문장을 수행하고 나서는 counter의 값이 2가 된다.

제일 처음에 있는 'counter = 1'이라는 문장은 제일 처음에 한번만 실행된다. 두 번째의 'counter <= 10'문장은 4번과 6번의 {와 }사이의 문장을 수행하기 전에 실행되면, 세 번째의 'counter++'문장은 4번과 6번의 {와 }사이의 문장을 수행한 다음에 실행을 하게 된다. 4, 5 그리고 6번 문장의 명령에 수행 순서를 그림으로 그리면 다음과 같이 그릴 수 있을 것이다.

 
예제 코드의 제어 흐름

어려울지 아닐지는 알 수 없다. 하지만 일단 이해하면 편안한 프로그래밍을 즐길 수 있을 것이다. 이번에는 다음 번 프로그램을 한번 입력해서 실행해 보도록 하자.

#include <stdio.h>
int main (int argc, char * argv[])
{
    int counter;
    printf ("난 1부터 10까지 셀 수 있어요 !\n");
    for (counter = 1; counter <= 10; counter++) {
        printf ("%d\n", counter);
    }
    printf ("어때요 ? 해냈죠 ?\n");
    return 0;
};

좀 바보같은 프로그램이긴 하지만 for 문장의 또 다른 이용 방법을 생각해 보는데 도움이 될 것이다. 반복하는 횟수를 세는 변수 counter를 단지 횟수만 세도록 할 뿐 아니라, 그 값 자체도 활용할 수 있다는 사실을 명백하게 해주는 프로그램이라 하겠다.

주의사항:

for 루프의 두 번째 항목의 조건은 '끝나는 조건'이 아니라, '계속하는 조건'이라는 것이다. 프로그램 반복을 끝내는 조건이 아니라, 계속하기 위한 조건이므로 주의해서 사용해야 실수가 없을 것이다. 흔히 잘 실수하고 착각하는 부분이다.

프로그램을 나누어서 작성하려면 함수를 이용한다

+/-

앞에서도 한 번 이야기를 꺼낸 적이 있는데, C에서는 '함수'라는 단위로 프로그램을 작성하게 된다. 물론 지금까지 사용해 왔던 printf () 나, scanf () 같은 시스템 라이브러리 함수를 사용해서 프로그램을 만들었었다. 그리고 이번에는 사용자가 직접 함수를 만들어서 사용하는 방법에 대해 설명하도록 할 것이다. 함수의 용도는 여러 가지가 있겠지만, 어떤 내용이 자주 반복되어 사용되는 경우에 많이 사용되고, 프로그램의 크기가 너무 커져버리는 경우에 그 크기를 작게 나누기 위해 사용되기도 한다. 물론 함수를 사용하면 프로그램을 이해하기 쉬워지는 건 물론이다. 일단은 다음의 프로그램을 입력해서 실행시켜 보자:

#include <stdio.h>
void say_hello ();
int main (int argc, char * argv[]) 
{
    say_hello ();
    say_hello ();
    return 0;
};

void say_hello ()
{
    printf ( "Hello !" );
};

이번에는 색다른 것이 몇 개 추가되었다. 일단 프로그램을 실행시키면 화면에 "Hello !"라는 글이 두 번 나타날 것이다. 물론 이렇게 복잡한 짓을 하지 않아도 똑같은 내용을 화면에 출력할 수 있을 것이다. 하지만 say_hello () 라는 함수의 내용이 한 줄이 아니라 수십, 수백 줄이라면 상황이 달라질 것이다. 또 이렇게 하는 경우, 프로그램이 이해하기 쉬워지게 된다. main 함수의 내용만 보아도 '어떻게 하는지는 모르지만 안녕이라고 두번 인사하겠구나'라고 프로그램의 동작을 예측할 수 있게된다. 그 다음, say_hello () 라는 함수의 내용을 바꾸려고 하는 경우, 같은 내용이 여기저기 반복해서 있는 경우보다 쉽게, 그리고 실수 없이 바꿀 수 있게된다.

이제 프로그램을 분석해 보자, 프로그램을 실행시키면 제일 먼저 5번 줄의 명령을 만나게 된다. 그럼 컴퓨터는 프로그램 안에 같은 이름을 갖는 함수가 있는지를 찾게 된다. 그럼 10번 줄에 같은 이름의 함수가 있다는 것을 확인하고, 그 내용을 실행하기 위해 10번 줄로 가게 된다. 그리고 12번 줄에 이르러 화면에 "Hello !"라고 인쇄 한 다음에, 13번 줄에서 명령이 끝났다는 것을 확인한 다음 다시 5번 줄로 돌아가게 된다. 5번 줄에 해당되는 명령은 이미 수행을 했으므로 이제 6번 줄의 명령을 확인하게 되고, 다시 10번 줄에 있는 함수의 내용을 수행하고, 7번 줄에 이르러 프로그램이 종료된다.

말로 설명했기 때문에 좀 복잡해 졌는데, 그림을 그려놓고 설명하면 조금 더 쉽게 이해할 수 있지 않을까 한다.

 
함수가 호출되는 순서

프로그램은 한번만 작성해놓고, 그 프로그램을 여러 번 부려먹을 수 있다는 뜻임을 쉽게 알 수 있으리라고 생각한다. 자, 이제 자세한 내용을 설명해 보도록 하겠다. 2번 줄과 10번 줄에 동일한 내용이 반복되어 있는 것을 볼 수 있을 것이다. 그러나 자세히 보면 2번 줄의 끝은 세미콜론';'으로 끝나고, 10번 줄의 끝은 대괄호'{'로 끝난다는 것을 쉽게 발견할 수 있을 것이다.

2번 줄의 내용은 함수를 '선언'한 것이고, 10번 ~ 13번 줄의 내용은 함수를 '정의'하는 것이다. 2번 줄에서는 '앞으로 이러이러한 함수를 만들어서 사용할 것이다.'라고 컴파일러에게 알려주는 역할을 하는데, 최근에 만들어진 컴파일러에서는 별 문제가 되지 않지만, 예전에는 이 선언을 해주지 않으면 '그런 함수는 없다'는 식의 에러 메시지를 받곤 했다. 그리고 10번 ~ 13번 줄에서는 실제 함수의 내용을 만들어 넣는 것이다.

자세한 내용은 나중에 다시 언급하도록 하겠고, 일단은 main함수 뒤에 사용자 정의 함수를 정의하려면, main함수 앞에 함수를 '선언'해 주어야 한다는 것을 기억해 두면 될 것이다.

함수를 사용하는 다른 예를 하나 보도록 하겠다:

#include <stdio.h>
void counting (int number);
int main (int argc, char *argv[])
{
    printf ("먼저 하나부터 열까지 셉니다.\n");
    counting (10);
    printf ("이번엔 하나부터 스물가지 세볼까요 ?\n");
    counting (20);
    return 0;
};

void counting (int number)
{
    int counter;
    printf("숫자를 1에서 %d까지 셉니다.\n");
    for(counter = 1; counter <= number; counter++)
    {
        printf ("%d ", counter);
    }
    printf("\n");
    return;
};

앞에서는 아무런 입력도 출력도 없는 단지 '뭔가를 하는' 함수를 배웠었다. 이렇게 '뭔가를 하는' 함수는 프로그램을 이해하기 쉽도록 간결하게 만드는 방법으로 사용되고, 여러군데에서 반복해서 같은 일을 하도록 하는 경우, 그리고 의 경우 프로그램을 한군데에만 수정하면 전부다 적용되기 때문에 수정을 쉽게 해준다는 함수의 기능중 몇가지만 사용할 수 있을 뿐이다. 하지만 이왕 한 코드를 반복해서 쓸 수 있도록 했으니 함수를 사용할 때 조건을 줄 수 있도록 하면 좋을 것이다. 위의 프로그램은 함수에서 인수를 받아 사용하는 아주 단순한 예이다. 위의 코드에서 볼 수 있듯, 호출된 함수 측에서 사용할 반복 횟수를 인수로 받아 반복횟수를 지정하는데 사용하고 있다. 이런 식으로 함수를 호출하는 쪽에서 호출되는 쪽으로 값을 넘겨주고자 할 때 인수를 사용하면 된다.

주석 및 참고자료

+/-
  1. 주: 컴파일 하고 실행하는 방법은 목차에서 자신의 환경에 맞는 페이지를 찾아 읽어보시기 바랍니다.
  2. 주: 이 파일의 위치는 시스템마다 다르게 지정되어 있기 때문에, 이 파일의 내용을 직접 보기 원한다면 사용중인 시스템과 컴파일러가 인클루드(include)파일을 어디에 저장하는지를 따로 확인 하셔야 합니다.
  3. 주: C외의 다른 프로그래밍 언어들에도 진입함수는 반드시 존재하기 마련입니다. 물론 함수 기반의 프로그래밍 언어가 아닌 경우에는 진입 함수가 아닌 진입점(entrance point) 혹은 다른 이름으로 불리우겠지만 여전히 동일한 역할을 하는 무엇인가가 존재합니다.
  4. 표준에 따라 정보의 종류만 먼저 나열하고 나중에 정보의 이름을 표기하는 경우도 있으나 최근의 표준들은 모두 정보의 종류와 함께 이름을 병기하고 있습니다.
  5. MS Visual C에서 작성된 C 프로그램의 메인 함수가 void 형의 리턴값을 갖는데, 함수의 리턴값이 void라는 의미는 함수가 돌려보내는 값이 없다는 의미이며, 윈도우의 경우 프롬프트 상에서 프로그램을 실행하는 경우가 거의 없기 때문에 실제적으로 main 함수의 리턴값이 의미가 없기 때문에 그렇게 된 것이 아닐까 싶습니다. 그러나 UNIX 계열의 운영체제에서 실행되는 프로그램의 경우에는 프로그램 자체가 쉡 스크립트 프로그램의 함수처럼 동작하는 경우가 대부분이기 때문에 정상적으로 다른 프로그램과 연동되어 동작할 수 있도록 하기 위해서 메인함수의 리턴값이 반드시 필요하게 되어 있습니다. UNIX 계열의 운영체제에서 main 함수의 리턴값은 반드시 int 로 쓰게 되어 있으며, 리턴할 수 있는 값은 -32768~32767 사이의 값으로 한정되어 있습니다. 이것과 관련된 내용은 시스템 콜의 system() 함수를 다룰 때 자세한 이야기를 할 수 있을 것 입니다.
  6. 변수 앞에 접두어를 붙이는 방법은 비교적 단순한 데이터 구조를 가지고 있는 환경에서는 유효 하지만, 데이터의 종류가 다양해지면 접두어로 붙이는 문자열 자체가 복잡해 져서 무의미해 질 수 도 있다는 단점을 가지고 있습니다.