C - 1.1. 시작

목차

1. 시작

이번에 여름방학을 맞아 초심으로 돌아가 C언어를 복습하면서 블로그에 그 내용을 정리하기로 했다. 다만 복습하면서 쓰는 것이므로 프로그래밍을 완전히 처음 시작하는 사람이 보기에는 배려가 부족할 수 있다.

교재는 흔히 C언어의 바이블 중 하나라고 꼽히는 K&R을 사용할 것이며 내가 지금까지 주워들었던 지식들도 중간중간 서술할 예정이다. 책 내용을 마무리하고 나면 링크드 리스트 등의 자료구조나, C언어로 구현할 만한 간단한 프로그램 등을 추가로 쓸 것이다.

이때 K&R에서는 터미널에서 컴파일하는 방식으로 가르치고 있으므로 나는 wsl에서 돌아가는 Ubuntu 터미널을 사용하여 글의 코드들을 실행할 것이다. 에디터로는 vscode를 사용할 것이다.

만약 당신이 특별히 사용하는 에디터나 IDE가 없다면, 흔히 C언어를 처음 시작할 때 사용하는 비주얼 스튜디오를 사용해도 좋을 것이다.

2. C언어의 구성

컴퓨터는 기본적으로 우리가 지시하는 연산을 순서대로 처리할 뿐인 기계이다. 그리고 C언어는 컴퓨터에게 우리가 원하는 연산을 지시할 수 있는 도구 중 하나이다. 이때 우리가 C언어로 내리는 지시는 얼마나 복잡한 지시건에 상관없이 함수(function)와 변수(variable)들로 이루어진다.

그럼 그 함수와 변수란 무엇일까? 함수는 어떤 작업과 어떤 연산을 할 것인지를 컴퓨터에 알려주는 문(statement)들로 구성되어 있는 요소이다. 그리고 변수는 그런 작업에 필요한 값들을 저장하고 있다. 개념만 설명하니까 굉장히 어려워 보인다. 쉬운 이해를 위해 예시를 들어 보자.

1+2를 계산한다고 생각해 보자. 거의 누구나가 3이라고 대답할 수 있을 것이다. 하지만 이를 뜯어서 생각해 보면 우리는 덧셈이라는 2항 연산을 1과 2를 대상으로 실행한 것이다.

즉 덧셈이라는 작업을 지시하는 것이 함수이고, 그 대상이 되는 값인 1과 2가 변수에 해당한다. 이런 식으로 변수에 있는 값들을 이용해서 함수에서 지시하는 작업을 하는 것이 바로 C언어 프로그램이다.

그러면 이제 긴 문법 설명을 하기 전에 먼저, 직접 프로그램을 작성해 보자.

3. 첫 프로그램

우리가 처음 작성할 프로그램은 hello, world를 출력해 주는 것이다. 일단 에디터에서 다음 문장을 따라 쳐서 hello.c라는 이름으로 저장해 주자.

#include <stdio.h>

main()
{
    printf("hello, world\n");
}
#include <stdio.h>

main()
{
    printf("hello, world\n");
}
#include <stdio.h>

main()
{
    printf("hello, world\n");
}
#include <stdio.h>

main()
{
    printf("hello, world\n");
}


그리고 나서 이를 gcc가 설치된 터미널에서 gcc hello.c 로 컴파일하고 ./a.out을 열어보면 hello, world 가 나오는 것을 볼 수 있다. 뭔가 나오긴 하는 것 같은데 대체 어떤 규칙에 따라 돌아가는 것인지 알 수가 없다. 한 줄씩 분석해 보자.

4. 첫 프로그램에 대한 설명

#include <stdio.h>

이 줄은 컴파일러에게, 표준 입출력 라이브러리(stdio는 standard input/output의 약자이다)를 쓰겠다고 알려 주는 것이다. 입출력 함수를 쓰게 해 주는 역할을 하는 부분이라고 생각해 주면 된다.

main()

C언어 프로그램의 실행은 언제나 메인함수부터 시작한다. 어떤 구조로 코드를 짜던지 간에 코드의 실행은 메인함수의 내용부터 실행된다. main의 내용 외에 다른 함수 내부에 있는 코드가 먼저 실행된다든지 하는 일은 없다.

이는 main이라는 이름의 함수에 대해 정해진 규칙이다. 따라서 일반적으로 함수의 이름은 자유롭게 정할 수 있지만 main이라는 이름은 예외이다. 즉 이 줄은 프로그램이 실행하는 내용의 시작을 알리는 것이라고 생각하면 된다.

또 함수의 내용은 함수 이름 뒤에 오는 중괄호 내부에 있다. 따라서 main() 뒤에 오는 중괄호 내부의 내용이, 프로그램이 실행하는 문(statement)이다. 그런데 main은 함수 이름이라고 치고, 그 뒤에 오는 소괄호는 뭘까?

그건 함수의 인자(argument)를 뜻한다. 함수들 간에 정보를 주고받는 방식은 함수 호출시에 인자를 전달하는 방식이다. main함수도 함수이므로 int main(int argc, char* argv[]) 등으로 main함수에 인자를 줄 수도 있다. 이런 방식은 나중에 보도록 하자.

printf("hello, world\n"); 함수 printf에 인자 "hello, world\n"을 전달하면서 호출한다. 이때 printf는 <stdio.h> 에 포함되어 있는 출력 함수이다. 인자로 받은 format string을 출력하는 것인데, 자세한 내용은 나중에 다루고 지금은 그냥 출력할 것을 인자로 받아서 출력해 주는 함수라고 생각하면 된다.

다만, 코드에서 이렇게 한 문장을 끝낼 때 세미콜론을 붙여 준다는 것만 주의깊게 보자. C언어에서 세미콜론은 자연어 문장에서의 마침표와 비슷한 기능을 한다.

5. 이스케이프 문자

위 코드의 "hello, world\n" 내에 있는 \n은 줄바꿈을 뜻한다. 줄바꿈 같은 문자는 쉽게 표현할 수 없으니 \를 붙여서 따로 정의하는 것이다. 만약 \n없이 프로그램을 실행하면 출력된 hello, world뒤에 줄바꿈이 없어지는 것을 볼 수 있을 것이다. 이때 이러한 \를 붙여서 따로 정의해 주는 문자들을 이스케이프 문자라고 한다.

C언어에서 이스케이프 문자를 사용하지 않고 줄바꿈을 표기할 수 있는 방법은 없다. 반드시 \n을 사용해서 표기해 주어야 한다. 만약 문자열 내에 줄바꿈을 그대로 넣어 주겠다는 생각으로 다음과 같은 코드를 짜서 실행한다면 바로 에러 코드를 보게 될 것이다.

printf("hello, world
");
printf("hello, world
");
printf("hello, world
");
printf("hello, world
");

물론 이스케이프 문자보다는 그냥 엔터키로 줄바꿈을 넣어 주는 것이 직관적이다. 따라서 좀더 최신의 언어들은 Python의 삼중 따옴표나 Javascript의 템플릿 리터럴처럼, 줄바꿈을 그대로 출력해 줄 수 있는 방식을 지원하기도 한다. 하지만 C같은 고전적인 언어에서는 그런 것을 지원하지 않는다. 이스케이프 문자밖에는 방법이 없다.

줄바꿈 외에도 탭(\t)이나 백스페이스(\b)와 같은 다른 이스케이프 문자들도 있다. 이스케이프 문자들에 대해서는 나중에 제대로 다룰 것이니 지금은 그런 것이 있다는 정도만 인식하고 넘어가면 된다.

6. printf의 출력

printf는 절대 자동으로 줄바꿈을 넣어 주지 않는다. 인수로 받은 문자열에 어떤 것도 추가하지 않고 그대로 출력해줄 뿐이다. 다소 고지식하게 느껴지기도 한다.

그러나 반면에 이런 점을 이용해 printf를 여러 번 호출하여 하나의 문자열을 나눠서 출력하는 것도 가능하다. 하나의 문자열을 여러 번의 printf를 이용해 나눠서 출력하는 방식의 코드를 쓰면서 첫 글을 마친다. 다음 코드를 실행할 시, hello, world\n 가 아까와 같이 잘 출력되는 것을 볼 수 있을 것이다.

#include <stdio.h>

main()
{
    printf("hello, wor");
    printf("ld\n");
}
#include <stdio.h>

main()
{
    printf("hello, wor");
    printf("ld\n");
}
#include <stdio.h>

main()
{
    printf("hello, wor");
    printf("ld\n");
}
#include <stdio.h>

main()
{
    printf("hello, wor");
    printf("ld\n");
}