Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/59.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/unix/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 这个程序是如何复制自己的?_C_Quine - Fatal编程技术网

C 这个程序是如何复制自己的?

C 这个程序是如何复制自己的?,c,quine,C,Quine,这段代码来自黑客的喜悦。上面说这是C语言中最短的程序,长度为64个字符,但我不明白: main(a){printf(a,34,a="main(a){printf(a,34,a=%c%s%c,34);}",34);} 我试着编译它。它编译时有3个警告,没有错误。它依赖于C语言的一些怪癖和(我认为是)未定义的行为 首先,它定义了main函数。声明没有返回类型或参数类型的函数是合法的,它们将被假定为int。这就是main(a){部分工作的原因 然后,它用4个参数调用printf。因为它没有原

这段代码来自黑客的喜悦。上面说这是C语言中最短的程序,长度为64个字符,但我不明白:

    main(a){printf(a,34,a="main(a){printf(a,34,a=%c%s%c,34);}",34);}

我试着编译它。它编译时有3个警告,没有错误。

它依赖于C语言的一些怪癖和(我认为是)未定义的行为

首先,它定义了
main
函数。声明没有返回类型或参数类型的函数是合法的,它们将被假定为
int
。这就是
main(a){
部分工作的原因

然后,它用4个参数调用
printf
。因为它没有原型,所以假定它返回
int
并接受
int
参数(除非编译器像Clang那样隐式声明它)

第一个参数假定为
int
,在程序开始时为
argc
。第二个参数为34(双引号字符为ASCII)。第三个参数是一个赋值表达式,它将格式字符串赋值给
a
并返回它。它依赖于指向int转换的指针,这在C中是合法的。最后一个参数是另一个数字形式的引号字符

在运行时,
%c
格式说明符被引号替换,
%s
被格式字符串替换,然后再次获得原始源代码

据我所知,参数求值顺序未定义。此quine之所以有效,是因为赋值
a=“main(a){printf(a,34,a=%c%s%c,34);}”
是在
a
作为第一个参数传递给
printf
之前进行计算的,但据我所知,没有强制执行它的规则。此外,这在64位平台上无法工作,因为指向int的指针转换将截断指向32位值的指针。事实上,尽管我可以看到它在som上的工作方式在e平台上,它不能在我的计算机上使用我的编译器。

程序应该打印自己的代码。请注意字符串文字与整个程序代码的相似性。其想法是,该文字将用作
printf()
格式字符串,因为它的值被分配给变量
a
(虽然在参数列表中)并且它也将作为要打印的字符串传递(因为赋值表达式的计算结果是赋值的值)。
34
是双引号字符(
)的ASCII代码;使用它可以避免包含转义文字引号字符的格式字符串

代码依赖于函数参数求值顺序形式的未指定行为。如果按参数列表顺序求值,则程序可能会失败,因为
a
的值将在实际为格式字符串指定正确值之前用作格式字符串的指针

此外,
a
的类型默认为
int
,并且不能保证
int
的宽度足以容纳对象指针而不截断它

此外,C标准只为
main()
指定了两个允许的签名,并且使用的签名不在其中


此外,
printf()的类型
编译器在没有原型的情况下推断出的结果是不正确的。这并不能保证编译器会生成一个适用于它的调用序列。

这是基于C允许你做的许多怪癖,以及一些对你有利的未定义行为而产生的。顺序:

main(a) { ...
如果未指定,则假定类型为
int
,因此这相当于:

int main(int a) { ...
即使
main
应该接受0或2个参数,并且这是未定义的行为,也可以忽略缺少的第二个参数

接下来是主体,我将其隔开。请注意,
a
是一个
int
,根据
main

printf(a,
       34,
       a = "main(a){printf(a,34,a=%c%s%c,34);}",
       34);
参数的求值顺序未定义,但我们依赖于第三个参数-赋值-首先求值。我们还依赖于能够将
char*
赋值给
int
的未定义行为。另外,请注意34是
的ASCII值。”
。因此,该计划的预期影响是:

int main(int a, char** ) {
    printf("main(a){printf(a,34,a=%c%s%c,34);}",
           '"',
           "main(a){printf(a,34,a=%c%s%c,34);}",
           '"');
    return 0; // also left off
}
在评估时,产生:

main(a){printf(a,34,a="main(a){printf(a,34,a=%c%s%c,34);}",34);}

这是原始程序。Tada!

此程序依赖于以下假设:

  • main
    的返回类型为
    int
  • 函数的参数类型默认为
    int
    ,并且
  • 将首先计算参数
    a=“main(a){printf(a,34,a=%c%s%c,34);}”
它将调用未定义的行为。函数参数的求值顺序在C中不受保证。
尽管如此,该计划的工作原理如下:

赋值表达式
a=“main(a){printf(a,34,a=%c%s%c,34);}”
将字符串
“main(a){printf(a,34,a=%c%s%c,34);}”
赋值到
a
,赋值表达式的值也将是
“main(a){printf(a,34,a=%c%s%c,34);}”

赋值运算符将值存储在由左操作数指定的对象中。赋值表达式在赋值[…]之后具有左操作数的值

考虑到赋值运算符的上述语义,程序将扩展为

 main(a){
      printf("main(a){printf(a,34,a=%c%s%c,34);}",34,a="main(a){printf(a,34,a=%c%s%c,34);}",34);
}  
ASCII
34
。说明符及其相应的参数:

%c ---> 34 
%s ---> "main(a){printf(a,34,a=%c%s%c,34);}" 
%c ---> 34  
更好的版本是

main(a){a="main(a){a=%c%s%c;printf(a,34,a,34);}";printf(a,34,a,34);}  

它比K&R C长了
4
个字符,但至少跟在K&R C后面。

3个警告和无错误意味着编译成功,为什么不运行它?@CESSARA问题不是它做什么-它是如何做的?因此,标题。这实际上不是最短的程序。实际最短的是0字节