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

时间:2011-10-29 08:30:19

标签: c function-pointers

我找到了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);
}

3 个答案:

答案 0 :(得分:264)

不要写那样的代码。


对于j<1000j/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/1000j < 1000字词向下舍入为0;一旦j达到1000,它就会计算为1.

现在,如果您有a + (b - a) * n,其中n为0或1,则a n == 0b 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 个参数时,它很可能会崩溃。 ..