Макрос, который вызывает себя, печатает сам?

Следующая программа выглядит как макрос C, который вызывает сам себя.

#define q(k)int puts();int main(){puts(#k"\nq("#k")");}
q(#define q(k)int puts();int main(){puts(#k"\nq("#k")");})

Он компилируется и работает нормально. Он распечатывает себя.

Этот код действительно C? То есть полагается ли он на что-либо за пределами стандартного C для правильной работы?

@devnull указал, что у этого вопроса похожая программа:

#define q(k)main(){return!puts(#k"\nq("#k")");}
q(#define q(k)main(){return!puts(#k"\nq("#k")");})

Эта программа полагается на что-нибудь за пределами стандартного C для правильной работы?

1 ответ

Решение

Первая программа - пример реализации квин на Си. На высоком уровне она определяет макрос q() который создает определение main() это печатает две строки. Первая строка является аргументом сама по себе, вторая строка является аргументом, заключенным в вызов q() сам. Итак, следующая программа:

#define q(k)int puts();int main(){puts(#k"\nq("#k")");}
q(foo)

Расширяется в:

int puts();int main(){puts("foo""\nq(""foo"")");}

При компиляции и запуске это приводит к выводу:

foo
q(foo)

Подставляя само определение макроса вместо foo приводит к Quine. Макрос на самом деле не вызывает сам себя, он вызывается для того же текста, который его определяет. В C макросы не раскрываются рекурсивно (C.99 §6.10.3.4 ¶2).

Как отмечено в вопросе, программа компилируется без жалоб в GCC с использованием строгих настроек C.99 (-pedantic -std=c99). Программа использует только стандартные функции C и соответствует как C.99, так и C.11.

  • Замена макроса (C.99 §6.10.3), с заменой аргумента (C.99 §6.10.3.1) и # оператор "stringifying" (C.99 §6.10.3.2).
  • Объявление функции с неопределенным списком аргументов (C.99 §6.7.5.3 ¶14).
  • Конкатенация буквенных строк (C.99 §5.1.1.2 ¶1).
  • По умолчанию main() возвращаемое значение (C.99 §5.1.2.2.3)1).

Особо следует отметить, что программа не использует ASCII-кодировку символов.

Программа будет компилироваться на компиляторе C.89-90, но поведение не возвращает значение из main() не определено для С.89-90. Программу можно легко изменить, чтобы она соответствовала требованиям C.89-90, добавив return 0; после звонка puts(),

Что касается второй программы, это также quine. Тем не менее, он не соответствует ни C.89-90, ни C.99, ни C.11. Это потому, что он опирается на puts() вернуть положительное число для оператора логического не, чтобы возвращаемое значение 0, Тем не менее, C требует только puts() вернуть неотрицательное значение в случае успеха (C.99 §7.19.7.10 №3). Только C.89-90 разрешает неявные объявления функций (C.89, §3.3.2.2). Программа может быть изменена в соответствии с C.89-90 путем удаления return!, а затем добавив return 0; после puts() вызов.

Структура этих программ в значительной степени вдохновлена ​​реализацией программы quine на "вымышленном" языке BlooP, представленной в книге Гёделя, Эшера, Баха: Вечная золотая коса, Дугласа Р. Хофштадтера (приписывают, что он ввел термин Куайн).

DEFINE PROCEDURE "ENIUQ" [TEMPLATE]: PRINT [TEMPLATE, LEFT-BRACKET,
QUOTE-MARK, TEMPLATE, QUOTE-MARK, RIGHT-BRACKET, PERIOD].
ENIUQ
['DEFINE PROCEDURE "ENIUQ" [TEMPLATE]: PRINT [TEMPLATE, LEFT-BRACKET,
QUOTE-MARK, TEMPLATE, QUOTE-MARK, RIGHT-BRACKET, PERIOD].
ENIUQ'].

Кроме того, вот версия программы, которая печатает свой исходный код в обратном порядке:

#define q(k)r(char*s){if(*s)r(s+1);putchar(*s);}main(){r(#k"\nq("#k")\n");}
q(#define q(k)r(char*s){if(*s)r(s+1);putchar(*s);}main(){r(#k"\nq("#k")\n");})
Другие вопросы по тегам