식, 문, 블록
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);
}
설명문(comment statement)
설명문은 프로그램의 이해를 도와주는 문장이다. C에 있어서의 주석문은 /*과 */으로 싸여진 문자열이고 행의 어느 부분에서도 작성이 가능하고 또 복수의 행에 걸쳐서 작성할 수 있다. 다음의 예는 설명문이다.
/* sample program */
/* program 1 2020.3.1. copyright*/
#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을 실행시켜 보세요. 그 결과는 어떻게 되지요?
/* 간단한 기초 함수 프로그램의 예 */
#include<stdio.h>
/* 함수 main( )의 정의 */
main()
{
line();
printf("*JUNG SUNG MIN*\n");
line();
}
/*함수 line( )의 정의 */
line()
{
intj;
for(j=1; j<=20; j++)
printf("*");
putchar('\n');
}
이 문제는 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');
}
/* 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() 함수의 원형에 대한 정보를 프로그램이 시작할 때 이미 컴파일러가 알고 있기 때문입니다.
0 댓글