문제 1) hello world!

 
우리는 문제 1)을 통해서 먼저 간단한 프로그램을 작성하도록 합시다.
문제1) hello world!를 출력하는 프로그램을 작성하시오.
 
해법1)
#include <stdio.h> void main() { printf("hello world!"); }
 
해답1)에 보시는 바와 같이 작성하면 hello world!를 화면에 출력합니다.
#include <stdio.h>와 같은 구문은 헤더화일을 포함시키는 기능을 합니다. 헤더화일은 미리 정의한 변수와 함수의 이름을 선언해 둔 파일입니다. 이 헤더화일이 존재해야만 현재의 파일에서 필요한 함수를 끌어 쓸 수 있습니다. printf() 함수가 바로 stdio.h 헤더화일에 선언되어 있는 함수입니다. 만약 stdio.h를 포함시키지 않았다면 printf() 함수를 사용할 수 없습니다. stdio.h를 포함시키지 않고 printf() 함수를 사용하게 되면 에러가 발생하게 됩니다. 우리는 이제 헤더화일이라는 하나의 요소 기술에 대해서 배웠습니다. 헤더화일을 왜 사용해야 하며 그것을 사용하지 않으면 어떤 문제가 일어나는지 확실히 알아두어야 할 것입니다. 여기서, 우리는 stdio.h 헤더화일은 C 컴파일러에서 기본적으로(디폴트로) 제공하는 시스템 정의 헤더화일임을 알고있어야 합니다. 물론, C 언어 명세에 의하면 printf() 함수를 C 언어의 구성요소로 간주하지 않고 외부 라이브러리로 언급하고 있습니다. 이 stdio.h 파일은 컴파일러 제작사(볼랜드사 또는 마이크로소프트사 등)에 의해 제공되는 파일들입니다. 이와 다르게 사용자(프로그래머)가 정의할 수 있는 헤더화일이 있는데 이 헤더화일을 사용자 정의 헤더화일이라고 합니다. 사용자 정의 헤더화일은 사용자가 정의한 함수들을 포함시키고자 할 때 주로 사용합니다.
그 다음으로 알아두어야 할 요소 기술로는 main()이라는 함수입니다. 이 main()이란 함수는 C 프로그래밍의 요체가 되는 함수로서 반드시 존재해야지만 프로그램을 만들 수 있습니다. 만약 main() 함수가 존재하지 않으면 C 프로그램을 만드는 것은 가능하지 않습니다. 그리고 프로그래머의 모든 기술사항의 중심에 해당하는 영역이 바로 이 main() 함수 내에 위치하게 됩니다. 프로그래머가 문제를 해결하기 위해서 해법을 기술하는 곳이 바로 이 main() 함수가 됩니다. main() 함수는 프로그래머의 문제 해결 내용들이 기술되는 곳이며 이 곳에서 다른 함수들을 호출할 수 있습니다. main() 함수는 나무에 비유하면 나무의 줄기에 해당합니다. 그리고 이 main() 함수라는 줄기로부터 많은 가지가 나올 수 있습니다. 즉, main()에 종속되는 함수들이 호출될 수 있습니다.
main() 함수에 대해서는 이 정도로 설명하고 다음으로 printf() 함수에 대해서 설명합니다.
prinf() 함수는 프로그래머가 정의한 함수가 아니라 컴파일러에서 제공하는 함수이며 단지 프로그래머는 이 함수를 끌어 쓴 것뿐입니다. 따라서, 프로그래머는 함수를 만들 필요 없이 만들어져 있는 함수를 사용했으므로 이렇게 컴파일러에서 제공하는 함수를 사용하는 것은 매우 효율적인 프로그래밍 방법이라고 할 수 있습니다. 이렇게 컴파일러에서 제공되는 함수를 라이브러리 함수 또는 내장함수(Built-in function)라고 하며 종종 시스템 정의 함수이나 컴파일러 정의 함수라고 말하기도 합니다. 모두 같은 뜻이므로 그렇게 이해해 두시면 됩니다. 여러분이 주목해야 할 사항은 이와 같은 printf()와 같은 함수가 라이브러리에 얼마나 존재하며 각각의 함수들은 어떤 기능을 하고 있는지 알아 둘 필요가 있다는 것입니다. 라이브러리 함수를 사용하게 되면 매우 프로그램의 생산성이 높아집니다. 생산성(Productivity)이 높다는 것은 프로그램을 적은 기간 내에 적은 프로그래머에 의해 쉽게 만들 수 있다는 뜻입니다. 그렇게 때문에 여러분은 시간이 나는 데로 C 컴파일러가 제공하는 라이브러리에 어떤 함수들이 제공되는지 눈여겨 보는 것이 중요합니다.
그리고 문장 끝에 세미콜론(;)을 찍어야 한다는 사실에 주의하십시요. C 언어에서는 반드시 문장 끝에 세밀콜론을 찍도록 규정하고 있습니다.
 
문제 1)에 대한 해법 1)을 통해서 문제가 어떻게 해결되는지 알 수 있었을 겁니다. 앞으로 본서는 이와 같은 형태로 진행될 겁니다. 이 방식은 문제-해법(Problem/Solution) 방식으로 알려져 있습니다. 특정한 주제를 지닌 문제를 대면함으로써 문제의식을 형성하게 되며, 이러한 문제의식을 해소시킴으로써 그 해소시키는 방법인 해법을 자신의 노하우로 축적시키게 되는 겁니다.
문제 1)에 대한 해답은 다양하게 존재합니다. 단지 하나의 해답만을 나타낸 것입니다. 다음의 해법을 한 번 살펴보십시오.
 
#include <stdio.h> void main() { char *ptr = "hello world!"; printf("%s",ptr); }
 
위의 해법은 ptr이라고 하는 문자열 포인터를 사용한 경우입니다. 문자열 포인터를 사용해서 출력할 경우 바로 위와 같이 프로그램을 작성하면 됩니다. 여기서는 요소 기술이 두 개 있는데 하나는 문자열 포인터를 정의하는 방법이고, 두 번째는 문자열 포인터를 printf() 함수에 매개변수로 넘겨주는 방법을 말합니다.
char* ptr = "hello world!"; 라는 문장(statement)은 다시 두 가지로 구분될 수 있습니다. 즉, char* ptr"hello world" 부분입니다.
char* ptrptr이라는 포인터 변수를 정의하는 것이며 할당문(=)은 "hello world"라는 문자열을 포인터 변수 ptr의 초기 값으로 설정합니다. 이때, 할당문 왼쪽에 있는 포인터 변수 ptrL-Value라고 하며, 할당문 우측에 있는 "hello world"R-Value라고 합니다. L-Value는 반드시 변수가 와야 하며, R-Value는 변수 혹은 상수(문자열,정수,실수,널 값)가 와야 합니다. L-Value라고 한 것은 Left Value를 의미하며 R-Value는 Right Value를 뜻합니다.
문자열 포인터를 printf() 함수에 매개변수로 넘겨주는 방법은
printf("%s",ptr);
와 같이 %s 포맷지정자를 사용하면 됩니다.
 
우리는 이와 같이 하나의 문제를 다양한 방식으로 해결할 수 있음을 알 수 있었습니다. 물론, 다양한 방식이라고 말했지만 지금까지 두 가지 방식을 말씀드렸습니다. 또 하나의 방식을 말씀드리면 다음과 같습니다.
 
#include <stdio.h> char *str_f() { char *ptr = "hello world!"; return ptr; } void main() { printf("%s",str_f()); }
 
위의 프로그램 코드는 함수를 사용하여 함수 내에 초기화된 포인터 변수 strreturn 문장을 사용해서 main() 함수 내에 반환해 주는 방식을 사용하고 있습니다. 이와 같이 str_f() 함수를 새롭게 정의해 줌으로써 이 함수를 호출하여 "hello world!"를 출력할 수 있습니다.
 
물론, 다음과 같은 해법도 가능합니다.
 
#include <stdio.h> char *str_f() { return "hello world!"; } void main() { printf("%s",str_f()); }
 
위의 방법은 포인터 변수 ptr을 제거하고 직접 "hello world!" 문자열을 str_f()함수의 return 문에 사용한 경우입니다.
 
우리는 생략, 함축, 새로운 기법, 분리 등 다양한 지적 도구(Intellectual Instrument)를 사용하여 하나의 문제를 다양한 방식으로 해결할 수 있음을 살펴볼 수 있었습니다. 실제 프로그램을 작성할 경우, 한가지 틀에 박힌 방식을 고집하기 보다는 다양한 방법을 알아두고 있다가 문제 해결에 가장 적합한 방식을 선택해서 적용하는 것이 중요합니다. 이 모든 해법이 모두 동일한 것이 아니기 때문입니다. 각각의 해법은 나름대로 장단점이 있으며 고유한 특징이 있습니다. 여러분이 주목해야 할 것은 문제의 다양성과 해법의 다양성이 지닌 미묘함을 이해하는 것입니다. 처음에는 커다란 윤곽을 파악하여 대강의 형태를 결정짓습니다. 그러니까, "hello world!" 문자열을 출력하기 위해서, 포인터를 사용할 것인가 아닌가를 결정하고, 만약 포인터를 사용한다면 이를 함수를 통해서 넘겨줄 것인가 아닌가 등을 결정해야 합니다. 이와 같이 다양한 조건과 경우의 수를 고려하여 문제 해법을 도출해 내야합니다.
 
문제는 다양한 상황을 만들고 다양한 조건을 이루어냅니다.
 
문제가 만약 '함수를 사용하여 "hello world!”를 출력하는 프로그램을 작성하시오'라고 했다면 해법은 반드시 함수를 사용해야 할 겁니다. 함수는 기본적으로 은닉화 개념을 사용합니다. 객체지향 언어인 C++에서 말하는 정보은닉(Information Hiding)개념의 기반이라고 할 수 있습니다. 은닉화란 함수의 내용을 외부에서 알 수 없다는 뜻입니다. 외부에서 예를 들어 main() 함수에서 str_f()함수를 호출할 경우, 대강 str_f()가 무슨 기능을 할 것인지 만을 알지 str_f() 함수가 정확하게 어떻게 구현되어 있는지까지 알 필요는 없습니다. 만약 함수가 매개변수로 리스트되는 것 이상을 프로그래머에게 알려주어야 한다면 함수를 사용하는 프로그래머는 엄청난 부담을 감수해야 할 겁니다. 이것은 부자연스러우며 효율적이지 못합니다.
프로그래머의 입장에서는 str_f() 함수가 매개변수와 return 값에 의해 무엇이 주고받아지는가만을 알면 될 겁니다. 실제로 str_f()함수를 구현할 때는 이점을 분명하게 인식하고 있어야 합니다. 함수를 사용하는 측에서 무엇을 전달해서 무엇을 전달받게 되는지 명시해 주어야 합니다. 이는 함수의 프로토타입(Prototype)의 도움을 받습니다.