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

Следующая программа выглядит как макрос 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 для правильной работы?


person jxh    schedule 01.08.2013    source источник
comment
Очень похоже на stackoverflow.com/questions/8200668/   -  person devnull    schedule 01.08.2013
comment
@devnull: я изменил вопрос, чтобы сделать его более заметным. Тот, на который вы ссылались, на самом деле не соответствует ни одному стандарту C.   -  person jxh    schedule 01.08.2013
comment
Ага! поэтому вы, кажется, предполагаете, что этот соответствует стандарту. Тогда что заставляет вас задавать тот же вопрос?   -  person devnull    schedule 01.08.2013
comment
Вы копируете вопрос и перефразируете ответы на него, и вам это нравится. Хороший!   -  person devnull    schedule 01.08.2013
comment
@devnull: Во-первых, это другой вопрос. Во-вторых, был только один ответ, и он вообще не говорил о соответствии. Да и описание какое-то неполное. Я не понимаю, как вы вообще можете думать, что я скопировал вопрос и перефразировал ответ.   -  person jxh    schedule 01.08.2013


Ответы (1)


Первая программа является примером реализации quine на языке C. На высоком уровне она определяет макрос 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) и оператором «строки» # (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() не возвращается, не определено для C.89-90. Программу можно легко изменить, чтобы она стала совместимой с C.89-90, добавив return 0; после вызова puts().

Что касается второй программы, то это тоже квайн. Однако он не соответствует ни 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().

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

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");})
person jxh    schedule 01.08.2013