메아리

Tour de IOCCC: 옛날의 C 언어

C 언어는 하루 이틀만에 만들어진 것이 아닙니다. 1972년에 데니스 리치(Dennis Ritchie)가 C 언어를 처음 만들었을 때 그는 유닉스 운영체제를 만들고 있었고, 유닉스가 널리 쓰이게 되면서 C 언어도 엄밀한 정의가 필요해지게 됩니다. 이에 부합하여 나온 책이 1978년의 《The C Programming Language》로 흔히 저자의 이니셜을 따서 K&R이라 하고, 이 책에서 정의하는 C 언어를 K&R C라고 부릅니다. 그 후 1990년에 C 언어가 국제 표준(ISO/IEC 9899:1990)이 되고, 1999년에 이 표준이 개정(ISO/IEC 9899:1999)되면서 현재 우리가 사용하는 C 언어가 만들어집니다.

IOCCC는 1984년에 시작되었습니다. 그 때는 당연히 국제 표준이 없었으므로 K&R C가 사실상의 표준이라고 할 수 있었는데, 이 언어는 물론 지금의 C 언어와 크게 다르지는 않지만 현대에 들어서 C를 배운 사람들을 놀래킬 기능들이 종종 있습니다. 심지어 호환성 때문에 당시 만들어진 기능들이 지금까지 유지되는 경우도 있고요. 이런 것들에 대해 알아 보는 것은 당시에 짜여진 코드, 특히 1999년 이전의 IOCCC 코드를 해석하는 데 중요합니다.

함수의 정의

K&R C가 현재의 표준 C에 비해 가장 크게 다른 점이라면 함수 정의 방법이 있겠습니다. 다음은 현재의 표준 C로 짠 간단한 프로그램입니다.

#include <stdio.h>

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

이걸 K&R C로 바꾸면 다음과 같이 됩니다.

#include <stdio.h>

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

함수 정의와 첫 중괄호 사이에 인자의 형을 마치 보통 변수 선언하듯이 선언하는 것을 볼 수 있습니다. 이건 아주 옛날, 그러니까 K&R이 나오기도 전의 초창기 C에서 다음과 같이 함수를 선언하던 것의 연장선에 있습니다.

main(argc, argv)
{
    printf("Hello, world!\n");
}

사실 이 선언은 너무 많이 쓰여서 K&R C는 물론, 1999년에 개정된 C 표준에서도 사용 가능할 정도입니다. 그래서 이런 형태의 함수 선언은 K&R 함수 선언이라고 하지 않고 K&R 스타일 함수 선언이라고 합니다.

이런 식으로 K&R 스타일 함수 선언을 쓰면 함수 호출은 상당히 자유로워집니다. 예를 들어 위의 main 함수에 인자를 다섯 개 주거나, 하나만 줘도 함수 호출 과정에서 오류를 발생시키지 않습니다! 선언된 이름보다 많은 인자를 주면 뒤의 인자들은 무시되고1 반대로 적은 인자를 주면 스택을 침범하는 등 위험한 행동을 하게 됩니다. 또한 인자에 형이 주어지지 않을 경우 그 인자의 형은 항상 int로 가정하게 됩니다.

그럼 이렇게 반문할 수 있겠습니다. "아래 코드는 동작하지 않아야 하는 거 아닌가요?"

main(argc, argv)
{
    printf("first argument = %s\n", argv[1]);
}

물론 그렇습니다! K&R C에서는 이런 코드를 허용하지 않습니다. (argvint니 배열처럼 접근이 불가능하겠지요.) 이 코드에는 char **argv;라는 형 선언이 꼭 필요합니다.

TODO


  1. 이런 특성은 표준 C가 나오기 전에 printf 같은 가변 인자 함수를 구현하는 데 사용되었습니다. 현재 사용되는 va_start와 같은 매크로들은 당시 사용하던 기법들의 흔적입니다.


Copyright © 1999–2009, Kang Seonghoon.