Friday, July 01, 2005

Chương trình tự tái sinh (3)

Ta thử dùng nguyên tắc mô tả trong ví dụ trước để viết một chương trình C tự in bản thân. Lần thử đầu tiên, chương trình A ghi x vào biến buffer, chương trình B in ra đoạn chương trình P(x), sau đó in x. Kết quả đại khái như sau:

int main() {
char* buffer = "printf(\"int main() {\\n char* buffer = %s;\\n %s\\n}\\n\", buffer, buffer);";
printf("int main() {\n char* buffer = \"%s\";\n %s\n}", buffer, buffer);
}

Lần thử này không thành công lắm, dù chương trình này in ra một chương trình gần giống nó. Vấn đề nằm ở cả đống ký tự thoát (escape characters '\'). Nó in thế này:

int main() {
char* buffer = "printf("int main() {\n char* buffer = %s;\n %s\n}\n", buffer, buffer);";
printf("int main() {\n char* buffer = %s;\n %s\n}\n", buffer, buffer);
}

Rút kinh nghiệm, tôi tránh các escape characters và thay chúng bằng mã ASCII. Mã ASCII của ký tự xuống dòng (linefeed) là 10, và của ký tự ngoặc kép (") là 34. Kết quả lần thử thứ hai cho đoạn chương trình:

int main() {
char* buffer = "printf(\"int main() {%c char* buffer = %c%s%c;%c %s%c}\", 10, 10, 34, buffer, 34, 10, buffer, 10);";
printf("int main() {%c char* buffer = %c%s%c;%c %s%c}", 10, 34, buffer, 34, 10, buffer, 10);
}

Chương trình này in ra

int main() {
char* buffer = "printf("int main() {%c char* buffer = %c%s%c;%c %s%c}", 10, 10, 34, buffer, 34, 10, buffer, 10);";
printf("int main() {%c char* buffer = %c%s%c;%c %s%c}", 10, 10, 34, buffer, 34, 10, buffer, 10);
}

Giống lắm rồi. Nhưng vẫn bị một escape character \ cho dấu " của printf. Lý do là vì cả printf lẫn định nghĩa buffer đều dùng ", mà nếu bỏ " vào trong định nghĩa buffer thì phải escape nó. Nhưng ta hoàn toàn có thể printf mà không dùng " như sau:

int main() {
char* buf1 = "char buf2[] = {'i', 'n', 't', ' ', 'm', 'a', 'i', 'n', '(', ')', ' ', '{', '%', 'c', ' ', 'c', 'h', 'a', 'r', '*', ' ', 'b', 'u', 'f', '1', ' ', '=', ' ', '%', 'c', '%', 's', '%', 'c', ';', '%', 'c', ' ', '%', 's', '%', 'c', '}', (char) 0}; printf(buf2, 10, 34, buf1, 34, 10, buf1, 10);";
char buf2[] = {'i', 'n', 't', ' ', 'm', 'a', 'i', 'n', '(', ')', ' ', '{', '%', 'c', ' ', 'c', 'h', 'a', 'r', '*', ' ', 'b', 'u', 'f', '1', ' ', '=', ' ', '%', 'c', '%', 's', '%', 'c', ';', '%', 'c', ' ', '%', 's', '%', 'c', '}', (char) 0}; printf(buf2, 10, 34, buf1, 34, 10, buf1, 10);
}

Bạn có thể dịch và chạy thử đoạn trên, in ra chương trình y hệt