간단한 C 프로그램 예
C 프로그램을 작성하는데 필요한 기본적인 규칙에 대해서 공부하기로 한다. 즉 C 프로그래밍 형식, 예약어와 표준 라이브러리 함수, 데이터 형과 선언, 연산자 등에 대해 공부한다. 예제 프로그램의 실행을 통해 C 언어를 학습하는 것이 본 자료의 기본적인 원칙이므로 Visual C++나 Turbo C 등의 각자 활용가능한 컴파일러를 구해 가능한 한 많은 프로그램을 실행해서 결과를 확인해보는 것이 최선의 선택이다. 먼저 첫 번째 예제 프로그램을 살펴보자. 예제 프로그램도 반드시 실행시켜 보고 그 결과를 확인하기 바란다. 앞으로 본 자료에서 설명할 프로그램과 내용 중에서 \ 문자는 \ (back slash)문자를 의미한다. 키보드 상에 \ 키 대신 \으로 나타나 있다.
#include <stdio.h>
main()
{
printf("Welcome to the C world!!!\n");
printf("이곳에는 밝은 미래와 희망이 가득합니다.\n");
printf("C와 함께 유익하고 즐거운 Computer의 세계로...\n");
}
그 결과는 무엇인가? 위의 프로그램에서 세 번 나오는 printf() 함수 사이의 " " 내의 문장들이 각각 다른 줄로화면에 출력된다. 그렇다면 다음과 같은 문제는 어떻게 해결할 수 있을까?
※ 다음과 같이 출력하는 C program을 작성하시오.
This is the 1-st C program.
^.^ :-) :-D :-' -<-@
I did it! Thank you.
C 언어를 전혀 모르는 사람들도 프로그램의 결과에 비추어 볼 때 나름대로 유추할 수 있을 것이다. 자신이 유추한 것과 아래의 해답과 비교해보자.
#include <stdio.h>
main()/* a simple C program */
{
int i;
i = 1;
printf("This is the %d-st C program.\n", i);
printf(" ^.^ :-) :-D :-' -<-@\n");
printf("I did it! Thank you.\n");
}
아마 다소 차이가 있는 경우가 있을 것이다. 단지 출력할 세 문장을 각각 printf()함수의 " "사이에 위치시킨 경우가대부분일 것이다. 물론 출력하고자 하는 문장 뒤에 \n표시는 빠트려서는 안 된다. 그러나 위와 같이 가능한 한 가지 해답의 예에서 추가된 것은 /*으로 시작하고 */으로 끝나는 문장이 먼저 보인다. 이것은 프로그램의 실행과는 아무 관계없고 단지 프로그램을 읽는 사람만을 위한 설명문(comment)이다. 그 외에 다소 차이가 있는 내용은 이제 우리가공부할 내용이니, 그냥 두기로 하자.
C 프로그래밍의 방식
C 언어의 자세한 문법에 대해서는 차츰 설명하기로 하고, 아래프로그램을 살펴보자.
예제
#include <stdio.h>
void like();
main( ) /* C Sample Program */
{
printf("I") ; /* 첫 번째 실행문 */
like(); /* 함수 like() 의 호출부 */
printf("programming language\n"); /* 세 번째 실행문 */
}
void like() /* 함수 like()의 정의부 */
{
printf("like C"); /* 두 번째 실행문 */
}
(1) main 함수
C의 프로그램은 함수의 집합으로 구성되어져 있다. 위의프로그램은 main 함수와 like 함수로 구성되어 있다. 더욱 간단한 프로그램은 main 함수로만 이루어져 있다. 프로그램의 실행은 이 main 함수에서 시작해서 main 함수로 종료된다. 프로그램에는 반드시 main 함수가 하나 있어야 한다. 함수의 본체는 { }로 이루어지며, 본문의 선두에 그 함수 내에서 사용하는 변수형을 선언한다. 함수의 호출은 단지 함수( )로 표시하면 된다. 앞 예제에서 like()는 함수 호출을 하는 부분이다.
(2) 함수와 리턴 값
C에서는 거의 모든 처리 단위가 함수로서 취급된다. 함수가 자신을 호출한 함수에 값을 전달하는 경우, 그 값을 리턴값이라고 한다. 위의 함수 like()는 리턴 값이 없는 함수이므로 이름 앞에 void라고 정의했다.
(3) 자유 형식 (free format)
C 프로그램을 살펴보면, 대문자와 소문자의 구분이 있고, 프로그램의 첫 머리가 왼쪽으로 맞춰 있지 않으며, 프로그램을 작성 할 경우 물리적인 라인의 제약을 받지 않는다. 즉,
main( )
{
printf("hello\n");
}
은
main ( ) {printf("hello\n");}
라고 표시해도 상관은 없다. 다만 후자의 방법보다는 전자의 표시 방법을 사용하는 것이 프로그램을 이해하는데 보다 편리하다. 명령어가 표현된 형태를 문장(statement)이라고 하는데, C 프로그램에서는 프로그램의 끝에 세미콜론(;)을 두고 문장의 끝을 표시한다. 이와 같이 물리적인 라인의 제약 없이 자유로이 프로그램을 작성하는 것을 자유 형식 (free format)이라고 한다. C 프로그램은 어디서부터 작성하여도 가능하므로 공백이나 탭(tab)을 적당히 사용하여 while이나 for 등의 루프 구조나 if∼else 등의 블록을 한 눈에 알 수 있도록 작성하면 된다.
(4) 식, 문, 블록
C 프로그램에는 식 (expression), 문 (statement), 블록 (block)의 개념이 있다. 일반적으로 식이란 일상적으로 이미 보아온 것과 동일한 형태로, 변수나정수를 연산자로 묶는 것으로
a * b + 10
와 같은 것이 대표적인 예이다. C에서는 = (equal)도연산자의 일종으로서 취급되나, 일상적인 의미인 좌측과 우측이 동일하다는 의미가 아니라(이런 의미의 연산자는 뒤에 '=='으로 주어질 것이다) 우측 식의 값을 좌측의 변수 값으로 지정(대입, 할당)하는 대입 연산자 (assignment operator)라 한다. 따라서,
y = a * b + 10
도 식이며, a * b + 10의 계산된 값이 y에 대입되어 그 값을 식의 값으로 가지고 있다. 그러므로 C에서는 다음과 같은 표현이 가능하다.
a = b = 5
이 식은 b=5라는 식이 5라는 값을 갖고 있기 때문에 이 값이 a에 대입된다.
어떤 사항을 서술하는 것을 문(statement)이라 한다. 식 에 세미콜론(;)을 붙이면 문이 된다. 이것을 특히 식으로 구성된 문이라고 하는데, 식으로 구성된 문 이외에도 if 문, for 문 등이 있다.
예를 들면 a = 5; 와 c = get char( ); 는 문이다.
문이 여러 개 모인 논리적인 한 덩어리를 블록(복문)이라하고 C에서는 { }로 표시한다. 블록이라는 개념이 있기 때문이나 while 문 등이 구조적으로 작성될 수 있다. 아래에 { }로 묶인 부분이 블록이 된다.
if(a>0) {
sum = sum = a;
printf("%d = \n",sum);
}
(5) 설명문 (comment statement)
설명문은 프로그램의 이해를 도와주는 문장이다. C에 있어서의 주석문은 /*과 */으로 싸여진 문자열이고 행의 어느 부분에서도 작성이 가능하고 또 복수의 행에 걸쳐서 작성할 수 있다. 다음의 예는 설명문이다.
/* sample program */
/* program 1 2000.9.8. copyright*/
(6) #include
#include는 선행 처리 제어문이다.
여기서는
#include <stdio.h>
에 의하여 이 위치에 stdio.h라는 이름의 파일을 불러와 삽입한다고 생각하자. stdio.h는 컴파일러 제작회사가 제공하는 표준 입출력 헤더 파일로 여러 가지 정의가 행하여진다. 이 파일은 get char/put char 등을 사용할 경우나 파일처리를 하고자 하는 때를 포함한 거의 대부분의 경우 반드시 포함되어야 한다.
함수 사용의 기초적인 내용은 위에 있는 대로입니다. 프로그램에서 함수를 어떻게 사용하는지 우선위의 예처럼 함수의 전달 인수를 사용하지 않는 간단한 함수 프로그램을 작성하여 어떻게 함수가 호출되고 실행되는지 앞으로 공부할 확인 문제 1의 간단한 예를 통해 설명합니다. 아래 문제 1은 ANSI C에서 권장하는 기본 형식을 따르지 않는 ANSI C 이전 형식의 프로그램이라는 것을 미리 밝힙니다. (ANSI C 형식을 따라 프로그램을 작성하는 것이 바람직합니다.)
문제 1
/* 간단한 기초 함수 프로그램의 예 */
#include<stdio.h>
/* 함수 main( )의 정의 */
main()
{
line();
printf("*JUNG SUNG MIN*\n");
line();
}
/*함수 line( )의 정의 */
line()
{
intj;
for(j=1; j<=20; j++)
printf("*");
putchar('\n');
}
확인 문제 1을 실행시켜 보세요. 그 결과는 어떻게 되지요? 이 문제는 main() 함수와 line()으로 구성된 프로그램입니다. main() 함수에서 프로그램은 시작되고, 첫 번째 실행문에 의해서 함수 line()을 호출합니다. 함수 line()이 호출되면 제어는 피 호출함수 line()으로 이동하는데, 이 때 운영체제는 함수 line()으로 이동하기 전에 함수 line()이 실행된 후 되돌아와서 실행될 다음 명령의 주소를 시스템 스택에 보관합니다. 따라서 피호출 함수에 있는 모든 명령문이 실행되어 함수가 종료되면 이제 스택에 저장된 주소를 이용하여 다음 번 수행할 명령문의 위치로 제어를 이동시켜 나머지 명령문을 실행합니다.
구체적으로는 프로그램은 main() 함수에서 시작되어 함수 호출문에 의해서 함수 line()을 호출하면 제어는 피호출 함수로 이동하여 함수 line()이 실행됩니다. 함수 line()은 for 문을 사용하여 printf() 함수를 20번 호출하여 '*' 문자를20개 출력하고 나서, put char() 매크로 함수를 사용하여 줄 바꿈을 하고 종료됩니다. 함수가 종료되면 피호출 함수는 제어를 자신을 호출한 함수로 반납합니다. 따라서 호출 main() 함수에서 첫번째 함수 호출문 다음에 있는 명령문인 printf() 함수가 실행됩니다. main() 함수의 마지막문은 함수 호출문 이므로 다시 함수 line()이 실행된 다음 함수line()이 제어를 main()에 반납하면 main() 함수에서는 함수의 끝에 도착했기 때문에 프로그램을 종료합니다. 너무 쉬운가요?
앞의 문제를 통해 함수가 어떻게 실행되는지 보았습니다. 어떤 함수를 실행하기 위해서는 프로그램 내부에 명확한 함수 호출문이 있어야 합니다. 이 때 함수 호출문에서 사용하는 함수명은 함수정의부에서 사용한 함수명과 동일해야 하며, 함수 호출문의 일반적인 형식은 다음과 같습니다.
함수명([실 인수 리스트]);
위의 형식에서 대괄호를 사용하여 묶인 부분은 생략할 수 있다는 뜻입니다. 즉, 함수 정의부에 있는 형식 인수로 데이터를 전달할 실 인수가 없을 때는 공백인 상태로 두면 됩니다. 이 때 주의할 것은 실인수의 유/무와 상관없이 한 쌍의 소괄호와 호출문의 끝을 나타내는 세미콜론은 반드시 있어야 합니다. 그리고 하나 이상의 실인수가 사용되면 순서 연산자(',')에 의해서 실인수를 서로 분리해야 합니다.
다음으로 함수 프로토타입(prototype) 즉, 원형과 void 형 함수의 기초에 대해 설명하겠습니다. ANSI C에서는 함수도 변수처럼 함수를 사용하기에 앞서 함수를 선언하도록 권장하고 있습니다. 함수 선언을 했을 때 얻을 수 있는 장점은 여러 가지가 있습니다. 여기서는 함수 선언을 어떻게 하고 void 형 함수란 무엇인지에 대해서만 알아봅시다.
함수의 선언은 프로그램에서 사용되는 함수의 원형을 사용 전에 미리 선언하는 것입니다. 함수의 선언을 함수 원형(prototype) 선언이라고도 하며, 함수원형 선언의 형식은 다음과 같습니다.
반환형 함수명(void 또는 각 전달 인수의 형);
만약 하나 이상의 전달 인수가 사용된다면 각 전달 인수의 자료형을 순서 연산자(',')를 사용하여 분리하면 되고, 전달 인수가 사용되지 않는다면 void를 사용하여 전달 인수가 없다는 것을 나타내면 됩니다. 함수 원형 선언은 호출 함수와 피호출 함수간의연결(interface)을 어떻게 할 것인가를 나타냅니다. 즉, 함수 원형은 함수 정의부에 기술된 함수에 대한 모든 정보를 컴파일러에게 알려줍니다. 컴파일러는 함수 원형 선언을 통하여 얻는 정보를 갖고 있으므로 함수의 호출부와 정의부 간에 전달되는 인수의 자료형이나 개수가 다른 경우나 반환형이 다른 경우에 함수 원형 선언에서 얻은 정보를 이용하여 이를 오류로 처리합니다. 따라서 전달 인수의 자료형이나 개수, 반환형이 서로 일치하지 않을 때 발생하는 모든 논리적 오류를 사전에 예방 할수 있습니다.
C언어에서 사용하는 모든 함수는 함수의 반환형을 지정하지 않으면 int형으로 내정됩니다. 즉, 자동적으로 함수의 반환형은 정수형으로 결정된다는 것입니다. 따라서 피 호출 함수가 실행된 후 실행 결과를 호출 함수에게 반환하지 않는 함수라 할지라도 반환형은 int형이 되며, 피 호출 함수는 알지 못하는 int형 데이터를 반환형으로 갖게 되지만 내정된 반환형이 int형이므로 아무런 문제없이 오류가 발생하지 않습니다.
ANSI C표준안에서는 이와 같은 모순을 해결하고자 void형 함수를 제안 했습니다.즉, 호출 함수로 값을 반환하지 않는다면 함수의 정의부에서 반환형을 지정할 때 이를 void형으로 지정하여 피 호출 함수가 예측할 수 없는 int형 데이터를 반환형으로 갖지 않도록 합니다. 그리고 ANSI C에서는 호출 함수와 피 호출 함수 간에 전달 인수를 사용하지 않는다면 전달 인수가 없음을 명확히 하고, 만약 잘못된 전달 인수가 사용된다면 이를 찾아내기 위해서 함수의 원형 선언부와 정의부의 전달 인수 목록에 void를 사용하도록 제안하고 있습니다. 이제 확인 문제 1을 ANSI C 표준안에 따라서 다시 작성하면 확인 문제 2와 같습니다. 확인 문제 1과 어떻게 다른가 자세히 살펴보기 바랍니다.
문제 2
/* ANSI C표준안을 사용한 간단한 기초 함수 프로그램의 예 */
#include<stdio.h>
/* 함수 main( )의 정의 */
void main(void)
{
void line(void);
line();
printf("*JUNG SUNG MIN*\n");
line();
}
/* 함수 line( )의 정의 */
void line(void)
{
int j;
for(j=1; j<=20; j++)
printf("*");
put char('\n');
}
확인 문제 2에서는 함수 line()이 모두 4번 나타나지요. 가장 첫번째 나타나는 것은 함수 line()의 원형 선언부입니다. 원형 선언에서 함수 line()은 호출 함수로 반환되는 값이 없음을 나타내고 있고, 전달인수도 역시 없다는 것입니다. 그리고 원형 선언은 선언문이므로 반드시 선언문 끝에 세미콜론이 필요합니다.
두번째, 세번째는 모두 함수 line()의 호출부입니다. 함수의 호출은 함수명에 의해서 호출되므로 함수명 앞에 void를 사용해서는 안 되고, 실인수가 없다고 할지라도 void를 사용해서는 안 됩니다. 만약 실 인수가 없으면 공백으로 비워두면 됩니다.
네번째는 함수 정의부입니다. 함수 정의부는 함수 원형 선언부와 동일하지만 정의부이므로 함수의 헤더(머리) 끝에 세미콜론을 붙여서는 안 됩니다. 컴파일러는 함수의 원형 선언과 정의부의 구분을 세미콜론 유/무에 따라 구분하므로 원형 선언에서는 세미콜론을 붙이지만 정의부의 헤더에서는 세미콜론을 붙이면 안된다는 것을 명심하기 바랍니다. 그리고 지금까지 사용된 main() 함수도 ANSI C 표준안에 따르기 위해서 함수의 반환형과 전달 인수를 사용하지 않는다면 다음과 같이 void를 사용하는 것이 바람직합니다.
void main(void)
앞으로 본 자료에서 main() 함수를 사용할 때 ANSI C 표준안을 따르지 않는 경우에도 여러분은 가능하면 ANSI C 표준안에 따라 main() 함수를 구성 하시 길 바랍니다. 그렇다면 main() 함수도 원형 선언이 필요할까요? 물론 main() 함수의 원형을 선언해도 상관없지만 사용하지 않는 것이 일반적인 관례입니다. 그 이유는 main() 함수는 프로그램을 시작하는 함수이므로 main() 함수의 원형에 대한 정보를 프로그램이 시작할 때 이미 컴파일러가 알고 있기 때문입니다.
댓글