Recommanded Free YOUTUBE Lecture: <% selectedImage[1] %>

Contents

Start C

Linux(:12)환경이 대략 구축되었으니, 이제 C언어 개발환경을 구축해 보도록 하자. 대부분의 Linux(:12) 배포판이 설치되는 즉시 완전한 개발환경(:12)을 구축해줄 테니, 본문의 내용을 읽고 확인하는 정도면 될 것이다. 물론.. 어떤 리눅스 배포판을 사용하고 있던지 간에, 패키지관리 툴정도는 다룰 수 있을 정도는 되어야 할 것이다.

필요한 요소

개발환경을 위해서는 최소한 아래의 툴들이 필요하다. 아래의 툴들은 완전히 공개되었으며 GPL(:12)을 따르는 소프트웨어들이다.
  1. 에디터 : 코드를 짤려면 당연히 에디터가 준비되어 있어야 한다.
  2. 컴파일러 : C언어에 의해서 짜여진 코드는 인간이 쉽게 이해할 수 있는 코드이다. 기계는 이 코드를 이해할 수 없으므로, 기계가 이해할 수 있는 기계어로 변경해줘야 한다. 컴파일러는 인간이 인지할 수 있는 C로 작성된 코드를 컴퓨터가 인지 기계어된 실행파일로 만들어준다. 번역기라고 생각하면 된다.
기본적으로 위의 2개의 툴만 설치되면 C(:12)언어를 배우는데, 전혀 문제가 없다. 그러나 C언어를 이용해서 그럴듯한 프로그램을 만들려면 몇가지 툴들이 더 갖추어진 환경을 만들 필요가 있다.
  1. 디버거 : C언어가 아무리 인간이 이해하기 쉽도록 만들어졌다고는 하지만, 여전히 기계(컴퓨터)의 입장에서 생각을 해야 한다. 그러다 보니 많은 실수가 생길 수 밖에 없다. 어떤 실수는 쉽게 찾아낼 수 있지만 어떤 실수는 찾아내기 매우 어렵다. 디버거를 이용하면, 잘못된 부분을 좀더 쉽게 찾아서 수정할 수 있다.
  2. Make툴 : 아주 작은 프로그램이 아닌 이상, 관리나 유지보수의 목적으로 여러개의 코드파일로 구성이 된다. make를 이용하면 이들 코드를 좀더 쉽게 유지할 수 있다. 프로젝트 관리를 위한 툴이라고 보면된다.
  3. 형상관리(:12)도구 : cvs(:12), svn(:12)등으로 공동작업을 할때, 코드 파일이 꼬이지 않도록 도와주며, 버젼을 관리할 수 있도록 해준다. 여기에서는 형상관리툴에 대해서는 다루지 않을 것이다. 아마도 꽤 큰 규모의 프로젝트를 하기전엔 필요없긴 하겠지만, 관심이 있다면 링크를 따라가서 읽어보기 바란다.

개발 환경 만들기

에디터 준비

코드를 만들려면 일단 에디터가 필요하다. Linux(:12)에도 다양한 에디터가 준비되어 있는데, 개인적으로 vi(:12)나 emacs(:12)를 사용할 것을 권장한다. 이들 에디터는 윈도우 사용자라면 익숙하지 않은 터미널 환경을 가지고 있다는 단점이 있다. 이들 에디터에 적응하기가 곤란하다면, 익숙해지기 전까지 울트라에디터와 같은 kate(:12)와 Visual C++과 같은 통합개발환경(:12)인 kdevelop(:12)등도 활용할 수 있다.

특히 kdevelop(:12)는 높은 수준의 통합개발환경을 제공한다. 그러나 kdevelop를 제대로 사용하기 위해서는, C언어 뿐만 아니라, 디버깅, 프로젝트/형상관리에 대한 내용을 알고 있어야 하기 때문에 지금 다루지는 않을 것이다. 혹시 자바언어를 사용했다면 eclipse(:12)에서 사용하능한 CDT(:12)라는 C/C++개발 환경도 제공한다. 역시 자세히 다루지는 않을 것이다.

여기에서는 vi를 사용하도록 할 것이다. vi를 사용하는 이유는 에디터로써의 필요한 기능만을 가지고 있기 때문에, 다른 부가적인 것에 신경쓰지 않고 학습에만 신경쓰면 되기 때문이다. 게다가 원격으로 연결해서 사용하기에도 전혀 문제가 없다 - GUI(:12) 방식의 에디터로도 원격작업을 할 수 있긴하지만.. 하지 않느니만 못한 경우가 대부분이다 -. 생소한 입력방식 때문에 처음에 적응하기 약간 까다롭겠지만, 눈 딱감고 한두시간 정도만 연습삼아 사용해 보기 바란다. 얼마안되어 vi마니아가 되어 있는 자신을 발견하게 될것이다.

컴파일러 준비

컴파일러는 인간의 언어에 가까운 C로된 코드를 번역해서 기계어(:12)로 만들어주는 일을 한다. 우리가 만든 코드를 실행가능한 프로그램으로 만들려면 반드시 컴파일러(:12)를 이용해서 컴파일 과정을 거쳐야 한다. Linux(:12)에는 강력한 gcc(:12)라는 컴파일러를 제공한다. gcc(:12)는 GNU 프로젝트의 결과물로 완전히 공개되어 있으며, 리눅스는 물론이고 거의 대부분의 상용 유닉스(:12)와 맥(:12) 윈도우즈에서도 사용할 수 있다. 때때로 gcc의 성능에 대해서 의문을 표하기도 하고 실제, 해당 벤더가 제공하는 전용의 컴파일러에 비해서 성능이 떨어지기도 하지만, 대부분의 경우 사용하는데 문제가 없다.

아뭏든 이 문서는 gcc를 기준으로 내용을 채워갈 것이다. 버젼은 3.x 이상으로 하겠다. 최근의 Linux에 설치된 gcc는 최소 3.x에서 4.x 버젼이니 사용하는데 큰 문제가 없을 것이다. 아래와 같은 방법으로 gcc가 설치되어 있는지 확인해 보도록 하자. 만약 gcc가 설치되어 있지 않다면, 배포판에 맞는 패키지 관리자를 통해서 설치해야 한다. 방법은 배포판에 따라서 다르기 때문에 별도로 설명하지 않도록 하겠다. 메뉴얼을 천천히 읽어보기 바란다.
# gcc --version
gcc (GCC) 3.2.2 20030222 (Red Hat Linux 3.2.2-5)
Copyright (C) 2002 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

첫번째 C 프로그램의 작성

C 프로그램이란 C언어를 이용해서 만들어진 프로그램을 뜻한다.

그럼 첫번째 프로그램을 작성해보도록 하자. 유명한 hello world프로그램으로, 실행시키면 화면에 hello world를 출력하는 일을 한다. 에디터로 아래의 코드를 입력해보자. 파일이름은 hello.c로 하자.
#include <stdio.h>

int main(int argc, char **argv)
{
    printf("Hello World\n");
    return 0;
}

간단히 설명을 하도록 하겠다. C언어 학습에 들어가기전에 맛보기 식으로 하는 거니, 이해가 안가더라도 그러려니 하고 넘어가도록 하자. 어차피 나중에 자세히 알아보게 될 것이다.
  1. #include : 외부 함수를 사용하기 위해서 함수가 선언되어 있는 파일을 포함시킬려고 사용한다. stdio.h는 표준입력/표준출력/표준에러와 관련된 함수들이 선언되어 있다.
  2. main 함수 : 프로그램의 본문이다. 프로그램이 실행되기 위해서는 반드시 하나의 main함수가 포함되어 있어야 한다.
  3. printf 함수 : 표준출력을위한 함수로 "Hello World\n"를 모니터 화면에 출력한다.
  4. return : 함수의 결과를 넘겨주기 위해서 사용한다.
이제 gcc 컴파일러를 이용해서 실행가능한 파일로 만들어 보도록 하자.
# gcc -o hello hello.c
hello.c를 컴파일 해서 hello라는 실행파일로 만들어라는 명령이다. 이제 hello를 실행하면 다음과 같이 주어진 일을하는 것을 볼 수 있을 것이다.
# ./hello
Hello World
#
현재 디렉토리에서 명령을 찾도록하기 위해서 ./를 이용했다.

C 프로그램의 구조

모든 프로그램은 특유의 구조를 가지게 된다. 앞 절에서 예로 들었던 hello.c를 이용해서 C 프로그램의 구조에 대해서 알아보도록 하겠다.
  1. 프로그램은 하나 이상의 함수로 이루어진다.
  2. 반드시 하나의 main함수를 포함해야 한다.
  3. 함수는 서로 독립적인 관계에 있다.
함수
함수는 주어진 일을 수행하는 코드조각으로 개념적으로 매우 간단하다.
    입력 데이터
    +--\ /--------------+
    |                   |
    |                   |
    +-----------/ \-----+
             출력결과
위의 이미지는 함수의 개념을 전형적으로 설명해주고 있다. 이미 초등학교때 소개된 개념이므로 함수를 이해하는데에는 전혀 문제 없으리라 생각된다.

함수는 어떤 데이터 셋을 집어 넣으면 필요한 연산을 해서 출력 결과를 되돌려준다. 함수는 다음과 같은 구조를 가진다.
return type function_name(argument)
{
   // 코드
}
  1. return type : 출력 데이터의 형(type)를 지정해 준다.
  2. function_name : 함수의 이름으로 각 함수는 이름으로 구분되며, 이름으로 사용할 수 있다.
  3. argument(인자) : 함수에 넘겨지는 값이다.
  4. 함수 코드 : 함수의 본체부분으로 인자를 받아서 연산을 하고 결과값을 되돌려준다.
다음은 두개의 숫자를 입력받아서 더하기연산을 한 후 되돌려주는 가장 간단한 형식의 함수다. 가장 간단한 함수이긴 하지만 모든 함수는 결국 아래의 예와 동일한 구조를 가진다.
int plus(int a, int b) 
{
	return a+b;
}

하나의 프로그램은 하나 이상의 함수로 구성되어있다. 이 함수들은 입력값과 출력의 형식으로 다른 함수들에게 값을 넘기는 식의 데이터의 흐름으로 주어진 일을 수행한다. 분업화된 컨테이너 벨트를 생각하면 될거 같다.

attachment:function.png
main 함수
main 함수는 특별한 종류의 함수이며, 프로그램이 시작되는 지점이다. 모든 C 프로그램은 반드시 하나의 main 함수를 포함하고 있어야만 한다. 다른 함수와 마찬가지로 return type, function name, argument의 3요소로 이루어져 있다.

attachment:function2.png

다음은 덧셈연산을 하는 sum이라는 함수를 포함한 프로그램이다.
#include <stdio.h>

int sum(int a, int b)
{
	return a+b;
}
int main(int argc, char **argv)
{
	int result;
	result = sum(4, 5);
	return result;
}
완전한 프로그램이 되기 위해서 하나의 main함수를 가지고 있으며, main함수는 내부에서 sum이라는 함수를 호출해서 4와 5를 더하고 결과값을 리턴하고 프로그램을 종료한다. 프로그램이 종료되었으면 어떻게 리턴값을 확인할 수 있을까 ? 다음과 같이 쉘에서 확인 가능하다.
# ./sum
# echo $?
9
echo(1)는 화면에 출력을 하기 위해서 사용하는 쉘내부명령어이다. $?는 가장 최근 실행시킨 프로그램이 리턴한 값이 저장되는 쉘의 특별한 변수다. 어떻게 이런일이 가능한지는 아직은 몰라도 된다. 차차 알아나가게 될 것이다.

include

주석
주석(:12)은 부가적인 정보를 알려주기 위해서 사용되는 C언어의 요소다. C언어가 인간의 언어와 상당히 유사하긴 하지만, 여전히 이해하기 힘든면이 있다. 그러다 보니 다른 사람이 코드를 이해하기 힘들며, 심지어 프로그램을 만든 당사자 조차도, 시간이 지나면 왜 이코드를 작성했는지 잊어버리는 경우가 생긴다. 이는 프로그램의 유지보수를 힘들게 만드는 요인이 된다.

주석을 사용하면 이해하기 쉬운 코드를 만들 수 있다. 특히 여러명과 협력해서 작업해야 할경우 주석은 필수적인 요소다. 당연하지만 주석은 기계어로 번역이 되지 않는다. 다음은 주석의 예이다.
#include <stdio.h>

/*
 * 만든사람 : yundream
 * 하는일 : 두개의 인자를 더한 결과를 리턴한다.
 * 인자 : int a, int b
 */
int sum(int a, int b)
{
	return a+b;
}

// Main 함수 시작 
int main(int argc, char **argv)
{
	int result;
	result = sum(4, 5); // 두개의 인자를 더한다.
	return result;
}
주석은 "/* */"과 "//" 두개를 이용해서 작성할 수 있다. "/* */"는 블럭단위의 주석을 만들기 위해서 사용하며, //는 라인단위의 주석을 만들기 위해서 사용한다. 함수 전체에 대한 상세설명등은 /* */를 코드 중간중간 간단한 설명을 위해서 //를 사용한다.

C 프로그램이 만들어지는 과정

인간이 이해하기 쉬운 C언어를 이용해서 프로그램을 만들었다면, 이를 컴퓨터가 이해할 수 있는 기계어 파일로 번역해서 컴퓨터가 실행할 수 있는 실행파일의 형태로 만들어야 한다. 이러한 일을 하는 프로그램을 컴파일러라고 한다. 이미 우리는 hello world 프로그램 예제를 통해서, 컴파일러를 이용해서 실행파일을 만들고 이를 실행시키는 방법에 대해서 알아보았다. 여기에서는 어떠한 과정을 거쳐서 실행파일이 만들어지는지에 대해서 알아보도록 하겠다.

  1. 소스 코드 생성
인간이 이해할 수 있는 언어로 프로그램을 작성한다. 이것을 소스코드라고 하는데, 여기에는 컴퓨터에게 내릴 명령들이 포함되어 있다. 소스코드는 인간이 쉽게 이해할 수 있지만, 컴퓨터는 이해할 수 없기 때문에 컴퓨터가 이해할 수 있도록 번역하는 과정이 필요하다.

  1. Pre processor
컴파일러를 실행시키면 가장 먼저 pre compile를 수행한다. 프로그래머가 생성한 소스코드는 인간이 보다 쉽게 읽을 수 있도록 하기 위해서 include나 매크로등을 이용해서 코드가 축약되어 있다. pre compile는 축약된 내용을 컴파일러가 쉽게 해석할 수 있도록 풀어쓰는 과정이다.

  1. Assembly 코드의 생성
이제 풀어쓴 코드를 가장 원시적인 언어의 형태인 Assembly(:12)코드로 만들어준다. 어셈블리코드는 기계어와 1:1로 대응되기 때문에 일단 어셈블리코드로 성공적으로 만들어낸다면 쉽게 기계어형태로 변환할 수 있다.

  1. Object 파일의 생성
Assembly 코드가 만들어졌다면, 이제 이걸 기계어로 변환한다. 이렇게 해서 만들어진 파일을 object파일이라고 한다.

  1. linker
그러나 object파일이 생겼다고 바로 실행될 수 있는게 아니다. 프로그램으로써 실행하기 위해서는 운영체제가 제공하는 다른 여러가지 객체(기능)들과 연결(link)되어야 한다. link과정을 거치면 비로서 실행가능한 완전한 프로그램이 만들어지게 된다.