我找到了C
代码prints from 1 to 1000 without loops or conditionals:
但我不明白它是如何工作的。任何人都可以通过代码解释每一行吗?
#include <stdio.h>
#include <stdlib.h>
void main(int j) {
printf("%d\n", j);
(&main + (&exit - &main)*(j/1000))(j+1);
}
答案 0 :(得分:264)
对于j<1000
,j/1000
为零(整数除法)。所以:
(&main + (&exit - &main)*(j/1000))(j+1);
相当于:
(&main + (&exit - &main)*0)(j+1);
这是:
(&main)(j+1);
使用main
调用j+1
。
如果j == 1000
,则相同的行显示为:
(&main + (&exit - &main)*1)(j+1);
归结为
(&exit)(j+1);
哪个是exit(j+1)
并退出程序。
(&exit)(j+1)
和exit(j+1)
基本相同 - 引用C99§6.3.2.1/ 4:
函数指示符是具有函数类型的表达式。除非它是 sizeof运算符的操作数或一元&amp; operator ,一个函数指示符 类型“函数返回类型”将转换为类型为“指针”的表达式 函数返回类型“。
exit
是一个函数指示符。即使没有一元&
地址运算符,它也被视为指向函数的指针。 (&
只是明确了。)
函数调用在§6.5.2.2/ 1及以下内容中描述:
表示被调用函数的表达式应具有类型指向函数的指针返回void或返回除数组类型之外的对象类型。
所以exit(j+1)
是有效的,因为函数类型自动转换为指向函数的类型,(&exit)(j+1)
也可以显式转换为指向函数的指针类型。
话虽如此,上面的代码不符合(main
取两个参数或根本不参与),我认为&exit - &main
根据§6.5.6/ 9未定义:
当减去两个指针时,都指向同一个数组对象的元素,或者指向数组对象的最后一个元素的元素; ...
添加(&main + ...)
本身有效,可以使用,如果添加的数量为零,因为§6.5.6/ 7说:
出于这些运算符的目的,指向不是元素的对象的指针 数组的行为与指向长度为1的数组的第一个元素的指针相同 对象的类型作为其元素类型。
因此,向&main
添加零可以(但使用不多)。
答案 1 :(得分:41)
它使用递归,指针算法,并利用整数除法的舍入行为。
对于所有j/1000
,j < 1000
字词向下舍入为0;一旦j
达到1000,它就会计算为1.
现在,如果您有a + (b - a) * n
,其中n
为0或1,则a
n == 0
和b
n == 1
结束&main
1}}。使用main()
(地址&exit
)和a
代表b
和(&main + (&exit - &main) * (j/1000))
时,&main
这个词会在j
时返回&exit
{1}}低于1000,否则为j+1
。然后,生成的函数指针被赋予参数j
。
这整个结构导致递归行为:当main
低于1000时,j
递归调用自身;当exit
达到1000时,它会调用{{1}},使程序退出,退出代码为1001(这有点脏,但有效)。
答案 2 :(得分:0)
https://stackoverflow.com/a/7937813/6607497 解释了这一切,但对于不耐烦的人,这里是等效的(可读)代码:
#include <stdio.h>
#include <stdlib.h>
void main(int j) {
printf("%d\n", j);
if (i/1000 == 0)
main(j+1);
else
exit(j+1);
}
所以我想它的工作原理很明显。
唯一真正使用的技巧是“计算转到”(&main + (&exit - &main)*(j/1000)
),当 main
为零时计算为 j/1000
,否则为 exit
(实际上如果它是 {{ 1}}).
也许还要注意,程序将 1
误用为 argc
,因此当您向程序传递参数时,它的计数会有所不同,并且当您添加超过 2000 个参数时,它很可能会崩溃。 ..