没有循环或条件语句,从1打印到1000的 C 代码是如何工作的?

我找到了 从1打印到1000不带循环或条件C代码: 但是我不明白它是如何工作的。有人能通过代码并解释每一行吗?

#include <stdio.h>
#include <stdlib.h>


void main(int j) {
printf("%d\n", j);
(&main + (&exit - &main)*(j/1000))(j+1);
}
8843 次浏览

永远不要写这样的代码。


对于 j<1000j/1000是零(整数除法)。因此:

(&main + (&exit - &main)*(j/1000))(j+1);

等同于:

(&main + (&exit - &main)*0)(j+1);

那就是:

(&main)(j+1);

j+1调用 main

如果是 j == 1000,那么得到的线条相同:

(&main + (&exit - &main)*1)(j+1);

归根结底就是

(&exit)(j+1);

也就是 exit(j+1)离开了程序。


(&exit)(j+1)exit(j+1)本质上是一回事——引用 C996.3.2.1/4:

函数指示符是具有函数类型的表达式 Sizeof 操作符 或一元及操作员的操作数,这是一个函数指示符,具有 类型“ 函数返回类型函数返回类型”转换为具有类型“ < strong > 指针”的表达式 函数返回类型 ”.

exit是一个函数指示器。即使没有一元 & address-of 操作符,它也被视为函数的指针。(&只是让它变得明确。)

函数调用在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添加0是可以的(但是用处不大)。

它采用了递归、指针算法,并利用了整数除法的舍入行为。

对于所有的 j < 1000j/1000项四舍五入为0; 一旦 j达到1000,它的计算结果为1。

现在如果你有 a + (b - a) * n,其中 n是0或1,如果 n == 0,你得到 a,如果 n == 1,你得到 b。对于 ab,使用 &main(main()的地址)和 &exit,当 n3低于1000时,术语 n1返回 &main,否则返回 &exit。然后将得到的函数指针输入参数 n5。

整个结构导致了递归行为: 当 j低于1000时,main递归地调用自己; 当 j达到1000时,它调用 exit,使程序退出,退出代码为1001(这有点脏,但可以工作)。

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);
}

所以我想它的工作原理很明显。 唯一真正使用的技巧是“计算 goto”(&main + (&exit - &main)*(j/1000)) ,计算结果为 main,而 j/1000为零,或者 exit(实际上如果它是 1)。

也许还应该注意到,程序正在误用 argc作为 j,所以当你向程序传递参数时,它的计数会有所不同,当你添加超过2000个参数时,它很可能会崩溃..。