我发现打印出的C
代码从1到1000都没有循环或有条件:但是我不明白它是如何工作的。任何人都可以遍历代码并解释每一行吗?
#include <stdio.h>
#include <stdlib.h>
void main(int j) {
printf("%d\n", j);
(&main + (&exit - &main)*(j/1000))(j+1);
}
我发现打印出的C
代码从1到1000都没有循环或有条件:但是我不明白它是如何工作的。任何人都可以遍历代码并解释每一行吗?
#include <stdio.h>
#include <stdlib.h>
void main(int j) {
printf("%d\n", j);
(&main + (&exit - &main)*(j/1000))(j+1);
}
Answers:
对于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运算符或一元&运算符的操作数,否则类型为“ 函数返回类型 ” 的函数指示符将转换为具有类型为“ 函数返回类型的指针 ”的表达式。
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
是可以的(但使用不多)。
foo
是一个指针,&foo
是该指针的地址。在第二种情况下,foo
是一个数组,&foo
等效于foo。
((void(*[])()){main, exit})[j / 1000](j + 1);
&foo
与foo
数组不同。&foo
是指向数组foo
的指针,是指向第一个元素的指针。它们确实具有相同的价值。对于函数,fun
和&fun
都是函数的指针。
它使用递归,指针算法,并利用整数除法的舍入行为。
该项j/1000
对所有取整至0 j < 1000
; 一旦j
达到1000,则评估为1。
现在,如果您拥有a + (b - a) * n
,其中where n
为0或1,则最终得到a
if n == 0
和b
if n == 1
。使用&main
(的地址main()
)和&exit
for a
和b
,该术语在低于1000 时(&main + (&exit - &main) * (j/1000))
返回,否则返回。然后将结果函数指针送入参数。&main
j
&exit
j+1
整个构造导致递归行为:j
小于1000时,main
递归调用自身;否则,递归调用。当j
达到1000时,它将exit
改为调用,使程序以退出代码1001退出(这有点脏,但是可以使用)。
exit
,该函数将退出代码作为其参数,然后退出当前进程。此时,j为1000,因此j + 1等于1001,这成为退出代码。
main
使用C ++进行调用。