C/C++ программа, которая печатает собственный исходный код в качестве вывода
Википедия говорит, что это называется quine, и кто-то дал код ниже:
char*s="char*s=%c%s%c;main(){printf(s,34,s,34);}";main(){printf(s,34,s,34);}
Но, очевидно, вы должны добавить
#include <stdio.h> //corrected from #include <stdlib.h>
таким образом printf()
может работать
Буквально, так как вышеприведенная программа не печатала #include <stdio.h>
, это не решение (?)
Меня смущает буквальное требование "печатать свой собственный исходный код" и любые цели такого рода проблем, особенно на собеседованиях.
11 ответов
Хитрость в том, что большинство компиляторов будут компилироваться без необходимости включать stdio.h
,
Обычно они просто бросают предупреждение.
Основная цель вопросов об интервью о программах Quine обычно состоит в том, чтобы узнать, сталкивались ли вы с ними раньше. Они почти никогда не полезны в каком-либо другом смысле.
Приведенный выше код может быть скромно обновлен для создания C99-совместимой программы (согласно GCC) следующим образом:
компиляция
/usr/bin/gcc -O3 -g -std=c99 -Wall -Wextra -Wmissing-prototypes \
-Wstrict-prototypes -Wold-style-definition quine.c -o quine
Код
#include <stdio.h>
char*s="#include <stdio.h>%cchar*s=%c%s%c;%cint main(void){printf(s,10,34,s,34,10,10);}%c";
int main(void){printf(s,10,34,s,34,10,10);}
Обратите внимание, что это предполагает набор кодов, где "
это кодовая точка 34, а новая строка - это кодовая точка 10. Эта версия печатает новую строку в конце, в отличие от оригинала. Он также содержит #include <stdio.h>
это необходимо, и линии почти достаточно короткие, чтобы работать на SO без горизонтальной полосы прокрутки. Приложив немного больше усилий, это, несомненно, можно сделать достаточно коротким.
Тестовое задание
Кислотный тест для программы Quine:
./quine | diff quine.c -
Если есть разница между исходным кодом и выводом, об этом будет сообщено.
Практически полезное применение "квино-подобных" техник
Еще во времена моей юности я подготовил двуязычную программу "самовоспроизводства". Это была комбинация сценария оболочки и исходного кода Informix-4GL (I4GL). Одним из свойств, которые сделали это возможным, было то, что I4GL лечит { ... }
как комментарий, но оболочка воспринимает это как единицу перенаправления ввода / вывода. I4GL также имеет #...EOL
комментарии, как и оболочка. Сценарий оболочки в верхней части файла содержит данные и операции для создания сложной последовательности операций проверки на языке, который не поддерживает указатели. Данные контролировали, какие функции I4GL мы сгенерировали и как каждый из них был сгенерирован. Затем был скомпилирован код I4GL для проверки данных, импортируемых из внешнего источника данных на еженедельной основе.
Если вы запустили файл (назовите его file0.4gl
) в качестве сценария оболочки и захватил вывод (вызов этого file1.4gl
), а затем побежал file1.4gl
в качестве сценария оболочки и захватил вывод в file2.4gl
, два файла file1.4gl
а также file2.4gl
будет идентичным Тем не мение, file0.4gl
может отсутствовать весь сгенерированный код I4GL и до тех пор, пока сценарий оболочки 'comment' в верхней части файла не будет поврежден, он будет самовоспроизводиться.
Quine имеет некоторые глубинные корни в семантике с фиксированной запятой, связанной с языками программирования и выполнениями в целом. Они имеют некоторое значение, связанное с теоретической информатикой, но на практике у них нет цели.
Это своего рода вызов или уловка.
Буквальное требование - это как раз то, что вы сказали, буквально: у вас есть программа, ее выполнение выдает себя в качестве вывода. Не больше и не меньше, поэтому это считается фиксированной точкой: выполнение программы через семантику языка само по себе является выходом.
Так что, если вы выразите вычисление как функцию, вы получите
f(program, environment) = program
В случае quine среда считается пустой (у вас нет ничего в качестве входных данных, ни предварительно вычисленных ранее)
Вы также можете определить прототип printf вручную.
const char *a="const char *a=%c%s%c;int printf(const char*,...);int main(){printf(a,34,a,34);}";int printf(const char*,...);int main(){printf(a,34,a,34);}
Quine (Базовый самореплицирующийся код в C++`// Самовоспроизводящийся базовый код
[ http://www.nyx.net/~gthompso/quine.htm[ https://pastebin.com/2UkGbRPF
// Самовоспроизводящийся базовый код
#include <iostream> //1 line
#include <string> //2 line
using namespace std; //3 line
//4 line
int main(int argc, char* argv[]) //5th line
{
char q = 34; //7th line
string l[] = { //8th line ---- code will pause here and will resume later in 3rd for loop
" ",
"#include <iostream> //1 line ",
"#include <string> //2 line ",
"using namespace std; //3 line ",
" //4 line ",
"int main(int argc, char* argv[]) //5th line ",
"{",
" char q = 34; //7th line ",
" string l[] = { //8th line ",
" }; //9th resume printing end part of code ", //3rd loop starts printing from here
" for(int i = 0; i < 9; i++) //10th first half code ",
" cout << l[i] << endl; //11th line",
" for(int i = 0; i < 18; i++) //12th whole code ",
" cout << l[0] + q + l[i] + q + ',' << endl; 13th line",
" for(int i = 9; i < 18; i++) //14th last part of code",
" cout << l[i] << endl; //15th line",
" return 0; //16th line",
"} //17th line",
}; //9th resume printing end part of code
for(int i = 0; i < 9; i++) //10th first half code
cout << l[i] << endl; //11th line
for(int i = 0; i < 18; i++) //12th whole code
cout << l[0] + q + l[i] + q + ',' << endl; 13th line
for(int i = 9; i < 18; i++) //14th last part of code
cout << l[i] << endl; //15th line
return 0; //16th line
} //17th line
Вот версия, которая будет принята компиляторами C++:
#include<stdio.h>
const char*s="#include<stdio.h>%cconst char*s=%c%s%c;int main(int,char**){printf(s,10,34,s,34);return 0;}";int main(int,char**){printf(s,10,34,s,34);return 0;}
тестовый забег:
$ /usr/bin/g++ -o quine quine.cpp
$ ./quine | diff quine.cpp - && echo 'it is a quine' || echo 'it is not a quine'
it is a quine
Строка s
содержит в основном копию источника, за исключением содержимого s
сам по себе - вместо этого он имеет %c%s%c
там.
Хитрость в том, что в printf
вызов, строка s
используется как формат и как замена для %s
, Это вызывает printf
положить это также в определение s
(в выходном тексте, то есть)
дополнительный 10
а также 34
s соответствуют переводу строки и "
разделитель строк. Они вставлены printf
в качестве замены %c
с, потому что они потребуют дополнительного \
в строке формата, что приведет к различию строки форматирования и замены, поэтому хитрость больше не будет работать.
/* C/C++ code that shows its own source code without and with File line number and C/C++ code that shows its own file path name of the file. With Line numbers */
#include<stdio.h>
#include<string.h>
#include<iostream>
using namespace std;
#define SHOW_SOURCE_CODE
#define SHOW_SOURCE_FILE_PATH
/// Above two lines are user defined Macros
int main(void) {
/* shows source code without File line number.
#ifdef SHOW_SOURCE_CODE
// We can append this code to any C program
// such that it prints its source code.
char c;
FILE *fp = fopen(__FILE__, "r");
do
{
c = fgetc(fp);
putchar(c);
}
while (c != EOF);
fclose(fp);
// We can append this code to any C program
// such that it prints its source code.
#endif
*/
#ifdef SHOW_SOURCE_FILE_PATH
/// Prints location of C this C code.
printf("%s \n",__FILE__);
#endif
#ifdef SHOW_SOURCE_CODE
/// We can append this code to any C program
/// such that it prints its source code with line number.
unsigned long ln = 0;
FILE *fp = fopen(__FILE__, "r");
int prev = '\n';
int c; // Use int here, not char
while((c=getc(fp))!=EOF) {
if (prev == '\n'){
printf("%05lu ", ++ln);
}
putchar(c);
prev = c;
}
if (prev != '\n') {
putchar('\n'); /// print a \n for input that lacks a final \n
}
printf("lines num: %lu\n", ln);
fclose(fp);
/// We can append this code to any C program
/// such that it prints its source code with line number.
#endif
return 0;
}
Моя версия без использования %c:
#include <stdio.h>
#define S(x) #x
#define P(x) printf(S(S(%s)),x)
int main(){char y[5][300]={
S(#include <stdio.h>),
S(#define S(x) #x),
S(#define P(x) printf(S(S(%s)),x)),
S(int main(){char y[5][300]={),
S(};puts(y[0]);puts(y[1]);puts(y[2]);puts(y[3]);P(y[0]);putchar(',');puts(S());P(y[1]);putchar(',');puts(S());P(y[2]);putchar(',');puts(S());P(y[3]);putchar(',');puts(S());P(y[4]);puts(S());fputs(y[4],stdout);})
};puts(y[0]);puts(y[1]);puts(y[2]);puts(y[3]);P(y[0]);putchar(',');puts(S());P(y[1]);putchar(',');puts(S());P(y[2]);putchar(',');puts(S());P(y[3]);putchar(',');puts(S());P(y[4]);puts(S());fputs(y[4],stdout);}
Не уверен, что вы хотели получить ответ о том, как это сделать. Но это работает:
#include <cstdio>
int main () {char n[] = R"(#include <cstdio>
int main () {char n[] = R"(%s%c"; printf(n, n, 41); })"; printf(n, n, 41); }
Если вы игрок в гольф, это более миниатюрная версия:
#include<cstdio>
int main(){char n[]=R"(#include<cstdio>
int main(){char n[]=R"(%s%c";printf(n,n,41);})";printf(n,n,41);}
main(a){printf(a="main(a){printf(a=%c%s%c,34,a,34);}",34,a,34);}
#include<stdio.h>
int main(void)
{
char a[20],ch;
FILE *fp;
// __FILE__ Macro will store File Name to the array a[20]
sprintf(a,__FILE__);
// Opening the file in Read mode
fp=fopen(a,"r");
// Taking character by character from file,
// you can also use fgets() to take line by line
while((ch=fgetc(fp))!=EOF)
printf("%c",ch);
return 0;
}