C 언어 | C 언어 입문 | 처음하는 C 언어
프로그램의 시작점(entry point)이 되는 main() 함수의 작성과 printf() 함수를 사용하여 화면에 텍스트를 표시하는 방법을 설명한다.
첫 번째 프로그램
C 언어 프로그램은 복수의 명령을 모은 함수(function)라는 블럭이 실행 단위인 부품이 합쳐지고 구성되어 이루어 진다. 반드시 C 언어 프로그램은 하나 이상의 함수가 존재하고 이 함수에서 명령문이 작성되어 있다. 함수에는 여러가지 프로그램의 흐름이 하나의 묶음으로 등록된다.
함수는 함수를 호출로 부터 어떤 정보를 받아 정보를 처리하고 그 결과를 반환할 수 있다. 예를 들어, 삼각형의 면적을 계산하는 함수를 만드는 경우, 함수는 삼각형의 밑변과 높이를 받는다. 이 때, 함수가 받는 정보를 인수라고 한다.
함수는 받은 인수부터, “밑변 × 높이 ÷ 2"를 구하고, 그 결과를 호출한 곳으로 반환한다. 이 함수가 반환하는 값을 반환 값이라고 한다. 여기서 프로그래밍은 수학이 아니기 때문에, 함수가 받은 값과 반환 값은 수치뿐만은 아니다. 문자열이거나, 값을 받지 않거나, 반환하지 않는 함수도 있을 수 있다. 중요한 것은 어떤 목적의 하나의 처리를 함수로 정리하고, 그것을 재사용(여러 번 호출)할 수 있다는 것이다. 함수에 대해 구체적으로 이해하기 위해서는, C 언어의 기본을 더 알아야 할 필요가 있다. 자세한 내용은 나중에 “함수"에 대해 설명할 것이기 때문에, 이 자리에서는 상기한 바와 같은 함수의 기본 개념을 이해한다.
함수를 작성하려면, 앞에서 설명한 인수와 반환 값의 지정 및 함수 이름을 지정한다. 그리고 함수는 중괄호 {}로 둘러싸인 명령문으로 구성된다.
함수의 정의
반환값의형식 함수명 (가인수...)
{
명령문
}
반환값의 형식은 값이 몇 바이트로 구성되는 것인가라고 하는 같은 정보를 컴파일러에 알리기 위한 것이다. 형식에 대한 자세한 내용은 곧 나중에 자세히 설명한다. 가인수…은 함수가 받아오는 값을 쉼표로 구분된 목록이다.
이와 같은 함수를 작성하는 것을 정의라고 한다. 그러나 이것만으로는 아직 실행되지 않는다. 함수는 호출되어야 처음으로 동작한다. 그럼, C 언어 프로그램은 어디부터 시작되는 것인가? 기본적으로 프로그램은 위에서 아래로 흘러간다. 그러나, C 언어가 실제로 실행되는 것은 main() 함수부터 시작하도록 되어 있다.
main() 함수 정의
int main(void)
{
명령문
}
따라서, 반드시 C 언어에는 main() 함수를 정의해야 한다. 반환값의 형식인 int는 숫자 데이터를 반환하는 것을 나타낸다. C 언어는 대소문자를 구별하기 위해 Main() 혹은 MAIN()라고 쓴다면 컴파일러는 인식하지 않기 때문에 주의해야 한다. 가인수…에는 값을 받지 않는다는 것을 나타내는 void를 지정하고 있다.
응용 프로그램을 기동하면 main() 함수 안의 명령이 순서대로 실행되고, 함수의 끝에 도달하면 응용 프로그램이 종료된다. main() 함수를 호출하는 것은 운영 체제 시스템이며, 반환 값은 운영 체제에 반환된다. 덧붙여서 운영 체제 시스템이 호출해야 하는 프로그램의 첫 번째 실행 지점을 응용 프로그램 엔트리(entry) 포인트라고 한다. 그럼 세계 제일 간단한 C 언어 프로그램을 작성해 보자.
코드1
int main()
{
return 0;
}
이 프로그램은 아무것도 하지 않고 즉시 종료하는 간단한 프로그램이다. 코드1를 실행하면 명령 프롬프트에 아무것도 표시되지 않는다. 우선 이 프로그램을 사용하여 컴파일러를 사용하여 제대로 컴파일할 수 있는지 여부를 확인해 보자. 코드에 오류가 없으면 오류 없이 빌드가 될 것이다.
컴퓨터, 그리고 프로그래밍 언어는 융통성이 없는 완고한 점이 있다. 우리 인간은 문장 속에 약간의 애매함과 마침표와 쉼표의 오타, 오탈자 등의 기술 실수가 있었다고 해도 앞뒤 문장의 흐름 등에서 상대의 의도를 추론하고 이해할 수 있다. 그러나 컴퓨터의 세계에서는 약간의 모호함과 기술 실수도 허용되지 않는다.
만약 코드에서 세미콜론(;)과 콜론(:)의 오타가 하나라도 있으면 오류로 빌드할 수 없다. 실제로 이러한 단순한 오타 초보자가 자주 머리를 아프게 하는 오류의 원인이 되고 있다. 만약 이 책의 샘플 프로그램을 열중하다가 오류가 발생한다면, 기호의 위치와 종류를 포함하여 정확히 입력되어 있는지 확인한다. 특히 괄호”(“와 중괄호 “{“의 실수와 특수 문자가 섞여있는 경우 발견이 어려울 수 있으므로 주의하도록 한다.
return은 함수를 종료하고 호출한 곳에 반환하는 것을 나타내는 문장이다. 즉, 이 프로그램은 실행 직후 return에 의해 제어를 호출한 곳에 반환하므로 즉시 종료하는 것이다. return 문은 다음과 같은 구문을 갖는다.
return [반환 값];
반환 값은 함수가 반환하는 값이 되는 식을 작성한다. 이 식의 형태는 함수를 정의하는 반환 형식과 호환이 되어야 한다.
텍스트 표시하기
다음은 화면에 문자를 표시하는 프로그램을 만들려고 하는데, 쇼킹한 것이 C 언어는 화면에 문자를 표시시키는 명령이 존재하지 않는다. 왜냐하면 하드웨어 다루는 처리는 프로그래밍 언어가 아니라 운영 체제의 역할이라고 생각하기 때문이다. 따라서 화면에 문자 출력은 운영 체제 서비스를 호출해야 한다. 그러나 화면에 문자를 표시하기 위해서만 그런 낮은 수준에 복잡한 것을 하는 것은 프로그래밍되지 않는다. 그래서 ANSI C 표준을 준수하는 컴파일러는 ANSI C 표준이 정하는 표준 라이브러리를 제공하고 있다. 표준 라이브러리는 입출력이나 복잡한 처리 등을 함수로 제공하고 있다. 우리 같은 프로그래머는 이 표준 함수를 이용하여 번거로운 작업을 생략할 수 있는 것이다.
프로그램이 의도한대로 작동하는지 확인하기 위해 결과를 화면에 표시해야 한다. 그래서 프로그래밍 언어를 학습할 때는 먼저 반드시 텍스트를 표시하는 방법을 배운다.
컴퓨터의 세계에서는 장비간에 데이터를 전달하는 것을 입출력(input/output)이라고 한다. 이 중에 다른 장비쪽으로 데이터를 송신하는 것을 출력이라고 하고, 다른 기기에서 데이터를 수신하는 것을 입력이라고 한다. 프로그램에서 다루고 있는 데이터를 화면에 표시하려면 데이터를 디스플레이에 출력해야 한다.
화면에 텍스트를 표시만 하는 프로그램이라면 매우 간단하다. C에서 한 줄의 코드만으로 임의 데이터를 출력할 수 있다.
printf("Hello, world!");
이것으로 화면에는 큰 따옴표 ““로 표시한 텍스트 Hello, world!가 표시된다. 이 코드에 포함되어 있는 의미를 제대로 이해하는 것은 쉬운 일이 아니다.
최초의 printf는 텍스트로 화면에 표시할 데이터를 출력하는 함수의 이름이다. printf() 함수에 데이터를 전달하면, 보통은 화면(Windows이면 명령 프롬프트)에 텍스트로 데이터가 표시될 것이다. 큰 따옴표로 묶인 부분 문자열이라고 한다. 문자열의 내용은 임의로 변경할 수 있다.
#include을 이용하여 헤더 추가
표준 라이브러리 함수를 사용하려면 소스 라이브러리 함수의 정의를 포함하지 않으면 안된다. 다른 소스 파일을 포함하려면 #include라는 전 처리기 지시문이라는 명령어를 사용한다. 전처리에 대해서는 뒷부분에서 자세히 설명하겠지만, 여기서는 #include를 사용하여 다른 소스 파일을 포함할 수 있다는 것만 알기로 하자. 참고로 다른 파일을 포함하는 것을 인클루드라고 한다.
#include "헤더 파일 이름"
#include <헤더 파일 이름>
#include 구문은 위의 두 가지가 있는데, 큰 따옴표는 일반적으로 자신의 라이브러리 파일을 포함하는 경우에 이용하며, 컴파일러는 인클루드를 지정한 파일과 같은 디렉터리를 검색한다. 괄호를 사용하면 컴파일러는 표준 라이브러리 파일이 위치하고 있는 디렉터리를 검색한다. 일반적으로 표준 라이브러리를 포함하는 경우는 후자의 <~>
를 사용한다.
이처럼 인클루드하여 이용되는 함수들을 정의한 라이브러리를 헤더 파일이라고 하고, “.H"를 확장자로 하여 저장되어 있다. 그 중에서도 앞으로 우리가 모든 프로그램에서 반드시 사용하게 될 라이브러리가 stdio.h 헤더 파일이다. 이 헤더 파일에는 문자를 화면에 표시하거나 파일 입출력 등의 기본적인 입출력을 지원하는 함수가 정의되어 있다. 다음 프로그램은 함수를 사용하여 화면에 문자를 표시한다.
코드2
#include <stdio.h>
int main() {
printf("Kitty on your lap\n");
return 0;
}
코드2는 명령 프롬프트 문자열을 표시하는 프로그램이다. 코드 맨 위에 #include <stdio.h>
가 추가되어 있는데, 이것은 printf() 함수의 기능을 사용하기 위해서 필요하다. 이 #include
명령은 컴파일시 <~>
안에 지정된 파일을 코드에 통합하는 작용이 있다. #include
명령으로 다른 소스 파일을 가져오는 것을 인클루드한다고 한다. 또한 stdio.h와 같은 표준 기능을 이용하기 위해서는 포함하는 파일을 헤더 파일(header file)이라고 한다.
코드2는 stdio.h라는 헤더 파일을 소스 파일의 시작 부분에 불러올 수 있다. 이 stdio.h 파일 안에 printf()
함수를 사용하기 위해서 필요한 코드가 포함되어 있다.
이 프로그램은 표준 함수의 하나인 printf() 함수를 호출한다. printf() 함수는 인수에 문자열을 전달할 수 있고, 함수는 이 문자를 화면에 표시한다. C 언어에서 문자열을 큰 따옴표로 묶는다. 큰 따옴표로 둘러싸인 문자열을 문자열 리터럴이라고 한다. 위의 프로그램에서는 “Kitty on your lap"가 리터럴 문자열이다. 그 결과, 이 프로그램은 콘솔에 Kitty on your lap 문자열을 표시한다. printf() 함수에 전달하는 리터럴 문자열의 내용을 자신이 좋아하는 문자열로 대체하여 콘솔에 문자열이 표시되는지 시도해 보자.
문자열의 끝에 있는 \n
는 줄바꿈을 나타낸다. Visual C++의 “디버깅하지 않고 시작"부터 기동하면, 프로그램 종료시에 “계속하려면 아무 키나 누르십시오.“라는 텍스트가 추가되므로 프로그램에서 출력된 결과의 레이아웃을 위해 사용하고 있을 뿐이다. 필요 없으면 생략해도 상관없다. 줄바꿈과 같은 눈에 보이지 않는 특수 문자를 처리하려면 이와 같이 기호를 사용하여 표현한다. 이러한 \
로 시작하는 문자열의 문자를 이스케이프 문자라고 한다. 문자로 \
를 표현하고 싶으면 \\
로 기술한다. 이스케이프 문자에 대한 자세한 내용은 앞으로 배우게 될 “상수"에서 자세히 설명한다.
또한 리터럴 문자열은 줄바꿈할 수 없다. 위의 프로그램을 다음과 같이하면 컴파일러는 오류를 발생한다. 줄바꿈을 표현하는 기호을 가진 이유가 이것으로 이해할 수 있다.
코드3
#include <stdio.h>
int main() {
printf("Kitty on your lap\n
");
return 0;
}
printf() 함수의 인수로 지정하는 문자열 리터럴이 도중에 줄바꿈을 하고 있다. 이 경우 컴파일러는 큰 따옴표가 닫혀 있지 않다고 인식 오류를 출력한다. 그렇다면 긴 문자열의 경우, 한 줄로 길게 쓸 수 밖에 없다. 물론 그래도, 프로그램이 올바르게 작동을 하긴 하지만 소스의 가독성 떨어진다는 단점이 있다. 이 경우 해결 방법은 두 가지가 있다.
하나는 개행 문자 앞에 \
기호를 붙인다. 이 경우 \
는 행 연속 문자로 기능하고, \
기호에 계속되는 개행 문자를 무시한다. 행 연속 문자는 문자열에서뿐만 아니라, 소스의 모든 곳에서 사용할 수 있다.
"Kitty on \
your lap";
또 다른 방법은 단순히 문자열을 분할하여 작성하는 방법이다. C 언어에서는 큰 따옴표가 분할되는 경우는 컴파일할 시에 이를 결합한다.
"Kitty on " "your lap"
예를 들어, 위의 공백으로 구분된 두 개의 문자열은 “Kitty on your lap"인 것으로 간주한다. 따라서 줄바꿈할 위치에 한번 문자열을 닫고, 개행 후에 문자열을 쓴다.
코드4
#include <stdio.h>
int main() {
printf("나 보기가 역겨워 가실때에는\n\
말없이 고이 보내드리오리다.\n");
printf("영변의 약산 진달래꽃 아름따다\n"
"가실길에 뿌리오리다\n");
printf("가시는 걸음걸음 놓은 그 꽃을\n");
printf("사뿐히 즈려밟고 가시옵소서\n");
return 0;
}
이 프로그램은 앞에서 제시한 방법을 사용하여, 문자열의 중간에 줄바꿈을 시도하고 있다. 첫번째 printf() 함수에서 행 연속 문자를 사용하여 개행 문자를 제거하는 방법을 사용하고 있다. 두 번째 printf()은 중간에 문자열 리터럴을 닫고, 개행 후에 다시 문자열 자세히 쓴는 방법을 사용하여 있다. 혹은 데이터마다 printf()로 출력해도 된다. 두번째의 방법이라면 문자열을 닫은 후에 자유롭게 탭 문자 등을 삽입할 수 있기 때문에 일반적으로 두번째나 세번째의 방법이 사용된다.