코딩/C와 C++

1편-예제를 통한 C 언어 기초 강의

드리프트 2020. 12. 29. 23:50
728x170

 

 

C는 프로그래머라면 가장 먼저 배우는 언어인데요.

 

저도 중학교 2학년때 부터 독학으로 Turbo C 책을 독파한 적이 있습니다.

 

정말 애증의 언어이며, 프로그래밍의 가장 기초적인 언어라고 생각합니다.

 

그래서 이번에 예제를 통한 C 언어 기초 강의를 시작해 볼까 합니다.

 

많은 관심 부탁드립니다.

 

먼저, 우리가 만들려고 하는 프로그램을 정해야 합니다.

 

목적이 있어야 과정이 생기니까요!

 

일단 1편이니까 간단하게 파일시스템의 IO와 커맨드라인 아규먼트 파서(parser)를 알아 볼까 싶습니다.

 

파일시스템과 Argument Parsing을 할 수 있는 프로그램으로 파일의 Hex코드를 볼수 있는 HexView를 만들어 볼까 합니다.

 

이번 강의는 어느 정도의 C언어에 대한 기본 지식이 있어야 되며, 그렇다고 고난이도 숙련 지식을 요구하지는 않습니다.

 

개발 환경은 맥북에서 VS Code로 진행할 예정입니다.

 

 

Setup

먼저 폴더를 만들고 hexview.c 파일을 만듭니다.

 

mkdir hexview
cd hexview
touch hexview.c

 

그리고 hexview.c  파일에 아래와 같이 기본적인 코드를 적용합니다.

 

#include <stdio.h>

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

 

이제 컴파일 해볼까요?

 

cc -o hexview hexview.c

 

컴파일이 성공했으면 실행해 보겠습니다.

 

./hexview
Hello hexview

 

여기서 cc는 C 컴파일러는 뜻합니다.

 

리눅스에서는 gcc가 있고 맥에서는 clang이 있고 윈도우즈에서는 마이크로소프트 C 컴파일러가 있습니다.

 

커맨드 라인에서 C 컴파일러를 처음 써 보신 분들은 어색할 수 있지만 기본적으로 어려운 점은 없습니다.

 

우리는 make 파일을 만들어 컴파일을 좀 더 쉽게 만들어 볼 예정입니다.

 

 

프로젝트 구조

먼저 이번 프로젝트의 폴더 구조를 정해 봅시다.

 

다음과 같이 입력하여 src 폴더를 만듭니다.

 

mkdir src
mv hexview.c src

 

mv 명령어는 move 명령어로 위에서 만든 hexview.c 파일을 src 폴더로 옮긴다는 뜻입니다.

 

이제 makefile 을 만들어 봅시다.

 

makefile을 만들어 두면 컴파일할 때 make만 입력하면 알아서 컴파일 해주는 좋은 도구입니다.

 

makefile 이란 파일을 만들고 아래 내용을 입력합니다.

 

hexview:
    @mkdir -p bin
    cc -o bin/hexview src/hexview.c

 

위 코드를 복사하여 붙혀넣기 하시면 에러 납니다.

 

왜냐하면 make 프로그램은 makefile에서 인덴트를 tab만 인식하기 때문에 무조건 tab을 사용하여야 합니다.

 

위 코드에서 두번째 줄이 첫번째 줄보다 떨어져 있는데 여기서 스페이스가 아닌 tab을 사용해야 합니다.

 

세번째 줄도 tab을 사용해야 하는 거는 당연합니다.

 

이제 우리는 make라고 터미널에 입력하고 실행하면 알아서 컴파일 해주는 오토빌드 시스템을 구축했습니다.

 

실행하면 아래 그림처럼 bin 폴더에 실행파일이 저장됩니다.

 

실행도 잘 되고 있습니다.

 

 

C 언어에서 많이 쓰는 makefile에 대해 간단히 알아 보겠습니다.

 

우리가 make라고 실행하면 make 프로그램은 makefile을 읽어 들입니다.

 

makefile에 뭐가 있었죠?

 

첫번째 줄은 다음과 같습니다.

 

hexview:

 

여기서 hexview: 는 타겟(target)이라고 합니다.

 

makefile은 복수의 타겟을 가질 수 있으며, 타겟을 지정하여 make로 하여금 특정 행동을 할 수 있게 합니다.

 

즉, 아래와 같이 입력하면 hexview 타겟을 실행하라는 뜻이 됩니다.

 

make hexview

 

hexview라는 타겟을 지정하지 않으면 make 프로그램은 기본적으로 makefile의 첫번째 타겟을 디폴트로 실행합니다.

 

그래서 우리는 그냥 make라고 입력해도 똑같은 결과가 나오는 겁니다.

 

2번째 줄은 bin 폴더를 만드는 shell 명령어입니다.

 

@mkdir -p bin

 

-p 옵션은 bin 폴더가 기존에 만들어 졌어도 에러메시지를 나타내지 말라는 옵션입니다.

 

@옵션은 mkdir 명령어가 실행될 때 터미널에 아무것도 나타내지 말라는 옵션입니다.

 

3번째 줄은 실제적인 컴파일 명령입니다.

 

cc -o bin/hexview src/hexview.c

 

-o 옵션은 output 파일을 지정하는 옵션입니다.

 

여기서는 output 파일로 bin 폴더에 hexview라는 파일로 지정했습니다.

 

그리고 마지막 src/hexview.c는 컴파일 할 대상을 적은겁니다.

 

지금까지 make 와 makefile을 통한 오토매틱 빌드 시스템을 구축했습니다.

 

 

라이브러리 추가하기

커맨드 라인 프로그램에서 가장 필요한게 커맨드라인 아규먼트(arguments)의 파서(parser)입니다.

 

아규먼트 파서는 꽤 많은 종류가 있으나, 필자가 발견한 가벼운 라이브러리를 소개해 드리고 이 프로젝트에 적용해 보겠습니다.

 

https://github.com/dmulholl/args

 

dmulholl/args

An argument-parsing library for C. Contribute to dmulholl/args development by creating an account on GitHub.

github.com

https://github.com/dmulholl/args/archive/master.zip

 

코드는 위 링크를 다운받아 압축을 풀고 여기서 우리가 필요한 두개의 파일만 발췌하면 됩니다.

 

우리가 필요한 파일은 args.c 와 args.h 입니다.

 

이 두개의 파일을 우리의 프로젝트 src 폴더로 복사하면 됩니다.

 

 

프로젝트 tree 구조를 보면 우리가 Github에서 다운 받은 args.c 와 args.h 파일이 src 폴더에 잘 들어가 있는 걸 볼 수 있습니다.

 

그럼 makefile을 수정해서 args.c 파일도 같이 컴파일 하게끔 수정해 보겠습니다.

 

hexview:
	@mkdir -p bin
	cc -o bin/hexview src/hexview.c src/args.c

 

make 를 실행해 보겠습니다.

 

 

에러없이 잘 컴파일 됐으며 실해도 잘 되고 있습니다.

 

그럼 이제 우리가 추가한 제3자 라이브러리인 args.c를 활용해서 커맨드라인 파싱에 대해 알아 보겠습니다.

 

C 기본 라이브러리에는 커맨드라인 아규먼트를 파싱하는 기능이 없습니다.

 

그래서 부득이하게 제3자 라이브러리를 이용할 수 밖에 없고 좋은 라이브러리를 잘 활용하면 시간을 많이 줄일 수 있습니다.

 

이제 커맨드라인 아규먼트 파서를 적용해 보겠습니다.

 

--help / -h 플래그나 --version / -v 플래그를 적용해 보겠습니다.

 

VS Code를 열어서 hexview.c 파일에 아래 코드를 입력합시다.

 

#include "args.h"

char* helptext =
    "Usage: hexview [file]\n"
    "\n"
    "Arguments:\n"
    "  [file]              File to read (default: STDIN).\n"
    "\n"
    "Options:\n"
    "  -l, --line <int>    Bytes per line in output (default: 16).\n"
    "  -n, --num <int>     Number of bytes to read (default: all).\n"
    "  -o, --offset <int>  Byte offset at which to begin reading.\n"
    "\n"
    "Flags:\n"
    "  -h, --help          Display this help text and exit.\n"
    "  -v, --version       Display the version number and exit.\n";

int main(int argc, char** argv) {
    // Instantiate a new ArgParser instance.
    ArgParser* parser = ap_new();
    ap_helptext(parser, helptext);
    ap_version(parser, "0.1.0");

    // Parse the command line arguments.
    ap_parse(parser, argc, argv);
    ap_free(parser);
}

 

실행해 보겠습니다.

 

 

-h 플래그가 잘 적용되고 있습니다.

 

그럼 코드 설명을 해보겠습니다.

 

char* helptext =
    "Usage: hexview [file]\n"
    "\n"
    "Arguments:\n"
    "  [file]              File to read (default: STDIN).\n"
    "\n"
    "Options:\n"
    "  -l, --line <int>    Bytes per line in output (default: 16).\n"
    "  -n, --num <int>     Number of bytes to read (default: all).\n"
    "  -o, --offset <int>  Byte offset at which to begin reading.\n"
    "\n"
    "Flags:\n"
    "  -h, --help          Display this help text and exit.\n"
    "  -v, --version       Display the version number and exit.\n";

 

위 코드는 커맨드라인 아규먼트 파싱을 위한 도움말을 char* 형태로 C 언어 스트링으로 만듭겁니다.

 

-h 라고 실행하면 출력되는 도움말 텍스인거죠.

 

C 언어에서는 멀티라인 스트링이 지원되지 않지만 C언어는 붙어있는 스트링 리터럴(여기서는 따옴표로 묶인 스트링 리터럴)은 합쳐서 인식하기 때문에 위의 helptext 스트링 리터럴은 커다랗고 긴 한줄의 문자열이 됩니다.

 

int main(int argc, char** argv) {
    // Instantiate a new ArgParser instance.
    ArgParser* parser = ap_new();
    ap_helptext(parser, helptext);
    ap_version(parser, "0.1.0");

    // Parse the command line arguments.
    ap_parse(parser, argc, argv);
    ap_free(parser);
}

 

main 코드를 살펴 보겠습니다.

 

여기서 ArgParser 는 우리가 적용하고자 하는 커맨드라인 아규먼트 파서인 제3자 라이브러리입니다.

 

먼저, ArgParser 구조체를 ap_new() 라고 실행해서 parser란 변수에 저장합니다.

 

그리고 ap_helptext() 함수를 이용해 parser 변수와 helptext를 연결합니다.

 

즉, helptext를 지정하는 거죠.

 

그리고 ap_version() 함수를 통해 parser 변수에 버전을 0.1.0으로 지정합니다.

 

그리고 본격적으로 파싱을 실행합니다.

 

ap_parse(parser, argc, argv) 라고 해서 parser라는 우리의 파서에게 이 프로그램의 커맨드라인 아규먼트인 argc와 argv를 넘겨줍니다.

 

그러면 ArgParser 구조체인 parser가 알아서 파싱하게 됩니다.

 

그리고 마지막으로 ap_free(parser)이라고 프로그램을 끝내기 전에 parser를 메모리 해제합니다.

 

코드는 github 예제를 통해 공부하시면 더 쉽습니다.

 

일단 여기까지는 어려운 점은 없습니다.

 

다음편에서는 본격적인 hexview 코딩에 들어가 보도록 하겠습니다.

그리드형