Proven C BookEnglish GitHub

19 프로그램의 구조

먼저 알아야 할 것

15장 헬로 월드 · 헬로 월드가 담고 있던 것들
13장 컴파일러 최적화 · 추상 기계의 계약

돌아보기

4장에서 기계는 “메모리에서 다음 할 일을 가져와 그대로 하는” 반복 이라고 했다. 그러면 소스 코드 쪽에서 그 “할 일 하나”에 해당하는 것은 무엇인가?

답. 문장(statement)이다. C 프로그램의 몸통은 문장들의 목록이고, 실행이란 그 목록을 위에서 아래로 한 문장씩 해치우는 것이다 — 4장의 계산 모델이 소스 코드에 그대로 비친 모습이다. 물론 13장에서 배웠듯 컴파일러가 속사정을 재배열할 수는 있지만, 겉보기 결과는 언제나 “위에서 아래로 한 문장씩”과 같도록 보장된다. 그래서 독자는 안심하고 이 그림으로 읽으면 된다.

이 장의 필요성과 맥락

4부의 목표는 하나뿐이다 — 15장의 헬로 월드를 완전히 읽어 내는 것. 그 목표를 넷으로 쪼갠 첫 조각이 뼈대다. 주석·문장·블록을 먼저 놓는 것은 나머지 세 장이 전부 「문장 안에서 일어나는 일」이기 때문이다. 담을 그릇을 먼저 만들고 안을 채운다.

이 장이 끝나면

제4부의 목표는 하나다 — 15장의 헬로 월드 한 편을 완전히 읽어 내는 것. 그 첫걸음으로 프로그램의 뼈대를 익힌다: 주석, 문장, 블록, 그리고 #include 줄이 문장이 아닌 이유까지. 이 장이 끝나면 C 소스의 겉모양이 더는 낯설지 않다.

이 장에서 답할 질문

  1. 왜 줄 끝을 문장 끝으로 삼지 않았는가? 세미콜론을 매번 찍는 것은 번거로운데.

19.1 주석 — 사람만 읽는 글

뼈대를 보기 전에, 뼈대가 아닌 것부터 치운다. 소스 코드에는 컴파일러가 완전히 무시하는 글— 주석(comment) — 을 적을 수 있다. 두 가지 표기가 있다:

// 이 표기는 여기부터 줄 끝까지가 주석이다
/* 이 표기는 여는 쪽과 닫는 쪽 사이가 주석이다 —
   여러 줄에 걸칠 수 있다 */

주석은 기계에게는 없는 것이고, 사람에게는 문서다. 이 책의 예제에도 설명을 주석으로 달아 두었다 — 코드와 함께 읽으면 된다.

19.2 문장 — 한 걸음, 그리고 세미콜론

문장은 프로그램의 한 걸음이다. 15장에서 본 printf(...) 줄과 return 0 줄이 각각 문장 하나다. 그리고 C에서 문장의 끝은 세미콜론 ;이 표시한다 — 우리글의 마침표다.

여기서 중요한 규칙 하나. C에서 줄바꿈은 문장의 끝이 아니다. 문장이 어디서 끝나는지는 오직 세미콜론이 정하고, 줄바꿈과 들여쓰기는 전부 사람 눈을 위한 정리 정돈일 뿐이다. 이런 언어를 자유 형식(free-form) 언어라 한다.

문. 왜 줄 끝을 문장 끝으로 삼지 않았는가? 세미콜론을 매번 찍는 것은 번거로운데.

답. 긴 문장 때문이다. 문장 하나가 길어져 여러 줄로 나눠 적고 싶을 때, “줄 끝 = 문장 끝”인 언어는 이어짐 표시 같은 예외 장치가 필요해진다. C는 반대쪽을 택했다 — 끝을 세미콜론으로 명시하고, 줄바꿈은 완전히 자유롭게 두는 것이다. 10장의 펀치카드를 기억하면 맛이 더 살아난다 — “한 장 = 한 줄”이라는 물리적 제약의 시대를 지나며, 언어는 줄이라는 형식에서 뜻을 독립시키는 쪽으로 진화했다.

흔한 오해. “한 줄에 한 문장씩 쓰는 것이 C의 규칙이다”

그럴듯하다 — 잘 쓰인 코드가 대개 그렇게 생겼으니까. 그러나 그것은 관행이지 규칙이 아니다. 문법상으로는 한 줄에 문장 여럿을 몰아 적어도 되고, 문장 하나를 다섯 줄에 걸쳐 적어도 된다. 컴파일러에게는 전부 같은 프로그램이다. 그런데도 “한 줄에 한 문장, 블록 안은 들여쓰기”라는 관행이 반세기를 살아남은 이유는 단순하다 — 코드는 기계보다 사람이 훨씬 자주 읽기 때문이다. 이 책의 예제도 그 관행을 따른다: 문법이 허락하는 자유와, 관행이 권하는 절제를 구별하는 것 — 이것도 C 공부의 일부다.

19.3 블록 — 문장들의 묶음

문장 여럿을 중괄호 { }로 감싼 것이 블록(block)이다. 15장에서 본 main 뒤의 담장이 바로 블록이었다 — main이 할 일의 목록을 하나로 묶은 것이다. 블록은 “여기부터 여기까지가 한 묶음”이라는 문법적 괄호로, 앞으로 만날 거의 모든 구조(조건, 반복, 함수)가 블록을 부품으로 쓴다. 지금은 “문장 목록의 포장 단위” 하나로 충분하다.

19.4 문장이 아닌 줄 — #의 세계

이제 15장 첫 줄의 마지막 비밀이다. #include <stdio.h> 끝에는 세미콜론이 없다. 문장이라면 마침표가 있어야 할 텐데 — 없는 이유는 간단하다. 문장이 아니기 때문이다.

16장의 릴레이를 떠올리면 된다. #으로 시작하는 줄은 첫 주자인 전처리기에게 하는 말이고, 전처리기는 C 문법이 아니라 단위로 일하는 텍스트 도구다. 그래서 # 줄의 문법은 C의 문장 규칙과 아예 다르다 — 세미콜론 대신 줄 끝이 지시의 끝이고, 관례상 파일 맨 위에 모여 산다. 한 소스 파일 안에 사실 두 개의 언어(전처리 지시의 언어와 C)가 함께 있는 셈이다. 낯설지만, 역사를 아는 우리에게는 사연이 보이는 구조다 — 전처리기는 별도의 일꾼으로 태어나 지금까지 남았다.

19.5 종합 — 두 문장짜리 프로그램

이 장의 재료를 전부 넣은 변형을 하나 시연한다. 15장 헬로 월드에서 바뀐 것은 하나 — 문장이 둘이 됐다.

examples/ch19/two.c

// 한 문장씩, 위에서 아래로 차례로 실행된다.
#include <stdio.h>

int main(void)
{
    printf("Hello, ");   /* 줄바꿈이 없다 — 다음 출력이 이어 붙는다 */
    printf("world!\n");  /* 줄바꿈은 여기, \n에서만 일어난다 */
    return 0;
}

실행 결과

Hello, world!

실행 결과가 한 줄인 것에 주목하면 된다. 문장은 둘, 걸음도 둘이지만 — 첫 문장에 \n이 없으므로 출력의 띠(10장) 위에서는 글자들이 그냥 이어 붙는다. 줄을 바꾸는 것은 문장이 아니라 \n이다 — 문장의 경계와 출력의 줄 경계는 서로 무관하다는 것, 이것이 이 시연의 교훈이다.

이제 헬로 월드 여섯 줄 중 뼈대는 다 읽힌다 — #include(전처리 지시), main과 담장(블록), 문장과 세미콜론, 그리고 주석까지. 남은 것은 담장 안쪽의 알맹이다: printf("...") 라는 문장 안에서 정확히 무슨 일이 벌어지는가. 다음 장에서 그 안쪽의 세계 — 값이 되는 것들, 수식 — 로 들어간다.