반응형

C언어의 토큰과 문장에 대해 설명한다. 코드에 적혀있는 모든 문자 순서와 기호는 토큰이라는 최소 단위로 분해할 수 있다. 여러 토큰 순서는 문장라는 작은 실행 단위이다.

코드의 최소 단위

아무리 복잡하게 작성된 프로그램도 토큰(token)이라는 최소 단위의 텍스트로 분해할 수 있다. 토큰은 더 이상 분해할 수 없는 프로그램의 최소 단위이며, 영어의 단어에 해당하는 것이다. 프로그래밍 언어의 명령이 되는 문장은 여러 토큰으로 구성되어 있다.

프로그래밍 언어를 기계어로 번역하기까지 여러 공정을 거쳐야 한다. 컴파일러에 입력된 텍스트는 소스 코드에 쓰여진 텍스트가 C언어에서 정하고 있는 문법을 준수하는지 여부를 확인하기 위해 토큰의 열에 분해된다. 이 공정을 어휘 분석이라고 한다.

토큰에는 여러 종류가 있으며, 프로그래밍 언어의 사양에 정해진 키워드, 조작 대상의 이름을 나타내는 식별자, 계산 기호 등의 연산자 등이 있다. 토큰은 다음 6 가지로 분류된다.

  • 식별자 (identifer)
  • 키워드 (keyword)
  • 상수 (constant)
  • 문자열 리터럴 (string-literal)
  • 연산자 (operator)
  • 구분자 (punctuator)

키워드는 C언어가 사양 레벨에서 예약되어 있는 이름(영단어)의 것으로, 이전의 프로그램에서 소개한 return 등은 키워드에 속한다. Visual C++ 같은 표준적인 개발 환경의 텍스트 편집기에서 파란색으로 표시된다.

식별자는 작업하는 데이터 및 명령을 식별하기 위해 소스 상에 명명된 이름이다. 예를 들어, main 등의 함수 이름은 식별자이다.

상수는 코드 상에 지정된 고정적인 값이다. 예를 들어 10과 3.14라는 같은 수치는 상수이다.

문자열 리터럴도 상수와 마찬가지로 코드 상의 고정 값에서 큰 따옴표로 묶인 여러 문자를 나타낸다. 문자열 리터럴은 코드에 텍스트를 삽입할 수 있다.

연산자는 계산을 위해 사용하는 기호으로써, + 기호와 - 기호 연산자에 속한다.

구분자는 [ ] ( ) { } * , : = ; ... # 같은 기호로, 어떤 요소를 구분하고 모와 정리하는 것을 나타내는 기호로 사용되고 있다. 일부 기호는 연산자와 동일하지만, 기호가 출현하는 위치에 따라 연산자인지 구분 기호인지를 확인할 수 있다. main 함수 이름 뒤에 괄호와 명령의 끝을 나타내는 세미콜론(;) 등은 구분자로 분류된다.

예를 들어, 아래 예제를 보도록 하자.

#include <stdio.h>

int main() {
    printf("Kitty on your lap\n");
    return 0;
}

이 프로그램은 다음과 같은 토큰 열로 구성되어 있다. 그러나 여기에 맨 위에 있는 #include부터 시작되는 행을 생각하지 않는다.

토큰토큰의 종류
int키워드
main식별자
(구분자
)구분자
{구분자
printf식별자
(연산자
"Kitty on your lap \ n"문자열 리터럴
)연산자
;구분자
return키워드
0상수
;구분자
}구분자

컴파일러는 이런 문장을 토큰 레벨로 분해하여, 하나 하나의 명령을 인식하고 있다. 한국어와 같은 인간이 사용하는 자연 언어에 비해 프로그래밍 언어에 불필요한 기호가 하나도 없다는 것을 알 수 있을 것이다. 프로그램 중의 모든 문자와 기호는 명확한 의미가 정의되어 있다. 거기에는 애매한 것은 없다.

각 토큰의 의미는 C언어 학습을 진행하면서 조금씩 이해해 나갈 것이다. 중요한 것은 소스 코드에 쓰는 모든 기호와 알파벳은 위의 토큰 중 하나로 분류할 수 있다는 것이다.

컴파일할시에 문법 등에서 에러가 나오는 경우는 작성한 코드가 올바른 토큰의 줄(line)인지 여부를 확인하여 오류를 쉽게 찾을 수 있을 것이다. 익숙한 기술자라면 의식하지 않고 토큰의 종류와 순서를 파악하고 컴파일러에 의존하지 않고 코드가 맞는지를 확인할 수 있다.

토큰 사이에 연산자와 구분자를 사용한 토큰이 있으면 구문에 따라 토큰은 분해되지만 키워드와 식별자, 상수 등이 연속적으로 계속되는 경우, 각각의 토큰은 공백으로 분리해야 한다. 정확히 공백(white space)이라는 부르는 다음 문자는 토큰을 분리할 수 있다.

  • 스페이스(space)
  • 수평 탭(\t)
  • 수직 탭(\v)
  • 개행, 줄 바꿈(\n\r)
  • 페이지 나누기(\f)

이 중에 수직 탭 및 페이지 나누기는 주로 프린터를 대상으로 한 전용 문자이며, 현재 일반적인 PC나 텍스트 편집기는 사용되지 않는다. Visual Studio에 의한 개발에서 사용하는 공백은 스페이스, 수평 탭, 개행 중 하나가 될 것이다.

코드1

    int
main (
void
){
        return
0;
    }

위의 코드1은 코드가 엉망이 되어있는 것처럼 보이지만 성공적으로 컴파일하고 실행할 수 있다. 공백이나 줄 바꿈이 제각각이므로 읽기 어렵지만, 각각의 단어나 기호의 줄을 토큰 열로 보면 올바르다는 것을 확인할 수 있다.

마찬가지로 토큰의 줄이 맞다면 수평 탭과 줄 바꿈없이 한 줄에 프로그램을 작성할 수 있습니다.

코드2

int main(){return 0;}

연산자와 구분자라는 기호의 토큰은 전후의 토큰을 구분하는 특성을 가지고 있다. 따라서 괄호()와 중괄호{}, 세미콜론; 등 전후의 토큰에는 공백이 없어도 문제 없다.

한편, 토큰 구분이 제대로 이루어지지 않은 경우 컴파일러 오류가 될 것이다. 예를 들어 다음과 같은 코드는 구문 오류다.

intmain(){return0;}

이 경우 시작 부분의 키워드 int과 함수 이름 main을 구분하지 않기 때문에 intmain라는 하나의 토큰으로 간주되어 버린다. 또한 함수 안에 return 키워드와 숫자 0 사이도 구분하지 않기 때문에 return0라는 토큰으로 해석된다. 컴파일러는 이 이름을 처리하지 못하고 오류를 보고할 수 있다.

반대로 구분해서는 안되는 부분에 공백이 삽입된 경우에도, 토큰이 추가로 분리되어 버려서 오류가 되어 버리는 것이다.

int mai n(){ retu rn 0; }

위의 코드는 함수 이름과 식별자 main 중간이 공백으로 구분되어 버려서 mai과 n이라는 두 개의 토큰으로 나누어져 있다. 컴파일러는 식별자 mai 직후에 나타난 n이 구문 부정이라고 판단하고 오류를 보고 한다. 마찬가지로 return 키워드를 공백으로 retu과 rn이라고 두 개의 토큰으로 분해되어 버리고 있다. 컴파일러는 retu과 rn라는 이름을 확인할 수 없기 때문에 역시 오류이다.

명령의 실행 단위

여러 토큰으로 이루어진 하나의 실행 단위를 문장 (statement)이라고 한다. 즉, 컴퓨터에 대한 명령은 문장 단위이며, 함수는 문장의 집합이라고 생각할 수 있다. 많은 문장은 세미콜론;으로 종료하기 위해 컴파일러는 세미콜론을 찾아내서 문장이 완료된 것으로 인식할 수 있다. 예를 들어 다음 프로그램은 2개의 문으로 이루어져 있다.

printf("Stand by Ready!!\n");
return 0;

화면에 텍스트를 표시하기 위해 printf() 함수의 행과 return 키워드로 시작하는 줄의 끝에 세미콜론; 기호가 있다. 이들은 여기에 문장이 종료하는 것을 나타낸다. 코드를 보기 쉽게하기 위해 문장이 끝나는 개행을 넣지만, 개행 자체는 의미를 가지지 않기 때문에, 원한다면 한 줄에 여러 문장을 작성할 수 있다.

코드3

#include <stdio.h>
int main() { printf("Stand by Ready!!\n"); return 0; }

코드3의 main() 함수에서 여러 문장을 줄 바꿈없이 기술하고 있지는데, 컴파일러는 세미콜론으로 문장의 끝을 인식할 수 있기 때문에 문제없이 컴파일 수 있다. 다만, 첫번째 행의 #include 전처리기 지시문(preprocessor directive)은 문장이 아니기 때문에 개행으로 종료시킬 필요가 있다.

문장이라고 해도 여러 종류가 존재하며, 크게 다음과 같이 분류되어 있다.

  • 명찰 부착 문장 (labeled-statement)
  • (표현)식 문 (expression-statement)
  • 복합 문 (compound-statement)
  • 선택문 (selection-statement)
  • 반복 문장 (iteration-statement)
  • 점프 문 (jump-statement)

이 중에 일반적인 계산이나 기능의 호출은 표현식 문로 분류된다. 함수 안에 기술된 문장을 많은 표현식 문으로 될 것이다. 표현식 문이나 점프 문 등은 끝에 세미콜론;를 붙이지 않으면 안되도록 정해져 있지만, 반드시 모든 문장 끝에 세미콜론이 추가되는 것은 아니다.

예를 들어, 복합 문장은 세미콜론을 붙이지 않는다. 복합 문은 함수 본체 (function-body)에 이용하고 있는 {}로, 블록이라고도 한다. 함수 본체는 하나의 복합문으로 생각할 수 있지만, 함수의 끝에;를 붙일 필요가 없는 이유는 복합 문장의 끝은 시작을 나타내는 중괄호 {에 대응 하는}으로 판단되기 때문이다.

각각의 문장이 구체적으로 무엇인지는 여기에서 순차적으로 설명하고 있다. 지금은 문장이 위와 같이 분류되어 있음을 기억해 두도록 한다.

반응형

+ Recent posts