Tour de IOCCC: 1994/smr
이 코드는 도대체 뭘까요? (힌트: 저는 실수하지 않았습니다.)
뭘 하는 프로그램인가?
이 프로그램은 세상에서 가장 작은 자기 복제 프로그램(자기 자신을 출력하는 프로그램)입니다. 이 프로그램보다 더 작은 건 없습니다. 0바이트거든요.
이 프로그램의 실행 과정은 당시 함께 배포된 Makefile로만 설명할 수 있습니다.
$ make smr
$ ./smr
$
물론 출력하는 게 없으니 자기 자신을 출력하는 셈이 됩니다.
왜 동작하는가?
이 프로그램은 일단 올바른 C 프로그램이 아닙니다. ISO/IEC 9899:1999(C99 표준)에 따르면 하나의 번역 단위(translation unit; 일반적으로 전처리기를 거친 소스 코드)는 하나 이상의 선언을 가져야 하기 때문입니다. 흥미로운 점은 전처리기에는 빈 파일이 들어 갈 수 있다는 것인데, 뭐 이런 차이는 그러려니 합시다.
올바른 C 프로그램이 아닌데다가 main 함수도 없으니 웬만해서는 컴파일이 될 리가 없습니다. 힌트 파일에는 일부 컴파일러들에서는 컴파일이 된다고 했지만 그 때는 그 때고 지금은 지금이죠. 하지만 저자가 "보장됨"(Guaranteed)이라는 말까지 써 가면서 당당하게 주장했던 것은 Makefile 때문입니다.
실제 Makefile에는 이렇게 쓰여 있습니다.
smr: smr.c
@${RM} -rf smr
${CP} smr.c smr
${CHMOD} +x smr
유닉스에 익숙하지 않은 사람을 위해 한 줄씩 설명하면,
- 맨 첫 줄은
smr
이라는 파일을 만들기 위해서는smr.c
라는 파일이 필요하다는 뜻입니다. (Makefile의 기본 구성 요소입니다.) 만약smr
파일이smr.c
파일보다 이전에 만들어졌으면smr
이 있더라도 뒤의 규칙들을 새로 실행하게 됩니다. - 다음 세 줄은 다음 세 가지 동작을 하라는 뜻의 유닉스 명령입니다.
smr
파일이 이미 있다면 지웁니다.smr.c
파일을smr
파일로 복사합니다.smr
파일을 실행 가능하도록 합니다. (윈도에서는 확장자로 실행 파일을 구분하지만 유닉스는 실행 가능함을 알리는 플래그가 따로 있습니다.)
따라서 이 Makefile은 "빈 실행파일"을 만들어서 문제를 해결한 것입니다. 위에서 봤듯이 빈 실행파일은 아무 문제 없이 잘 실행됩니다.
유닉스에서 실행 가능한(+x
퍼미션이 붙은) 파일은 보통 두 종류로 나뉩니다. 하나는 진짜 바이너리 파일로 ELF나 a.out과 같은 전용 포맷으로 되어 있으며 당연히 빈 파일이 될 수 없습니다. 다른 하나는 다른 지정된 프로그램을 통해 실행되도록 한 파일로 흔히 셸 스크립트나 스크립팅 언어로 된 프로그램이 여기에 속합니다. 이런 프로그램은 보통 다음과 같이 맨 앞에 #!
로 시작하는 줄로 어떤 프로그램이 필요한지 지정하곤 합니다.
#!/bin/cat
사실 이 파일도 실행 파일만 붙이면 프로그램입니다!
(cat는 지정된 파일의 내용을 보여 주는 유닉스 명령어입니다.)
하지만 #!
가 없는 경우에도 운영체제는 해당 파일을 셸 스크립트로 처리하도록 되어 있습니다. 이 동작은 POSIX의 exec 함수가 그렇게 동작하도록 되어 있기 때문인데, 빈 파일은 단순히 빈 셸 스크립트이기 때문에 실행이 가능한 것입니다.
이 프로그램은 사실 C보다는 유닉스의 어두운 구석을 찌르긴 했지만, 여하튼 심사위원들은 이 프로그램에 Worst Abuse of the Rules라는 이름의 상을 주었습니다. (소스 코드 크기에 최소 크기 제한은 없었으니) 그리고 다음 해 대회 가이드라인에서는 다음과 같은 내용을 볼 수 있었습니다...
We suggest that you avoid trying for the 'smallest self-replicating' source. The smallest, a zero byte entry, won in 1994.