【问题标题】:How does this code print a series of numbers in C?此代码如何在 C 中打印一系列数字?
【发布时间】:2014-07-06 01:36:27
【问题描述】:

这是一个基本的 C 程序,它不使用循环或条件来打印数字。我想知道它是如何做的,以及“退出”和“主要”的目的。 main 这里是用来递归的吗?

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

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

【问题讨论】:

  • 这是一个基本的多向错误 C 程序,使用带有 main 函数的递归...
  • 它使用了可怕的基于指针的递归。所以没有循环,但是有很多对 main() 的隐藏调用
  • 它是一个非常酷的递归版本。乘法类似于比较,如果 j 为 1000,则重定向到退出函数
  • 标准 C 中这段代码的确切问题是它违反了约束,因为 -+ 不能与函数指针一起使用。另外,当然,是否允许main 的签名是实现定义的。

标签: c


【解决方案1】:

假设 j 是 500。那么(&amp;exit - &amp;main)某事(无所谓),(j/1000) 是 0,所以 (&amp;main + (&amp;exit - &amp;main)*(j/1000))(j+1) 实际上是 main(501)。相反,如果j是1000,那么(j/1000)就是1,也就是说(&amp;main + (&amp;exit - &amp;main)*(j/1000))(&amp;main + (&amp;exit - &amp;main))是一样的(也就是&amp;exit),所以它会调用exit(1001)

【讨论】:

  • 如果程序最初是用 2000 个或更多参数调用的,那么所有的地狱都会崩溃。或者,至少,该行为比其他时间更加不确定。
  • @JonathanLeffler 是的,但这甚至没有进入“这不是好代码的原因”列表中的前 5 名。 ;)
  • 同意;代码非常糟糕,为程序提供 2000 个参数是一个有趣的练习:./a.out $(perl -e 'print "A " x 2000')。您可以通过使用:(&amp;main + (&amp;exit - &amp;main)*((j % 1000 + 1)/1000))) 来修复不确定性,但如果第一次调用时j 大于 1000,则当递归太深时程序可能会崩溃。所有这些都对可恶的事情进行了太多的分析。
  • @JonathanLeffler 不一定:只有int main(void)int main(int, char **) 的行为由标准指定。如果实现定义了void main(int) 的行为,那么参数不需要是命令行参数的数量
【解决方案2】:

我建议它应该如下工作:

  • j &lt; 1000 时,调用&amp;main + (&amp;exit - &amp;main)*(j/1000) 参数j+1。由于j/1000 == 0,它调用main(j+1)
  • j = 1000,由于j/1000 == 1,它调用&amp;main + (&amp;exit - &amp;main) * 1 == exit

然而,这个小小的 C 程序有很多错误。例如,void main(int) 不是标准的 main 签名。

【讨论】:

  • 提防微软——见What should main() return in C and C++?
  • +1 用于警告主函数原型以及实际问题的答案。
  • @JonathanLeffler:嗯,我不知道这种行为。 MS 是否也接受单个参数?
  • 否;不是官方的——据我所知,没有一个编译器只接受main 的一个int 参数。在实践中,被调用的函数通常会忽略额外的参数——所以它可以工作,但标准不保证它会工作。
【解决方案3】:

这个实际上编译为没有任何条件的程序集:

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

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

尝试:添加 '&' 这样它就会考虑地址从而避免指针错误。

上面的这个版本是标准C,因为它不依赖于函数指针的算术:

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

void f(int j)
{
    static void (*const ft[2])(int) = { f, exit };

    printf("%d\n", j);
    ft[j/1000](j + 1);
}

int main(int argc, char *argv[])
{
    f(1);
}

【讨论】:

    【解决方案4】:
    • &main 是 main 函数的地址(任何 C 的入口点 程序)
    • &exit是exit函数的地址(终止程序)

    考虑你的陈述:

    (&main + (&exit - &main)*(j/1000))(j+1);
    
    
    
    if j < 1000, then 
     (&exit - &main)*(j/1000) == 0 (integers division)
     // So we are calling main(j+1)
    else if j == 1000
     (&exit - &main)*(j/1000) == &exit - &main
     // So we are calling (&main + &exit - &main)(j+1), which is calling exit(j+1)
    

    所以你的程序递归调用 main() 直到 j == 1000,此时它调用 exit

    这个程序在编程/最佳实践/等方面非常错误......,你应该忘记它。

    【讨论】:

      猜你喜欢
      • 2017-12-01
      • 1970-01-01
      • 2017-01-19
      • 2012-09-14
      • 1970-01-01
      • 1970-01-01
      • 2017-02-28
      • 1970-01-01
      • 2019-05-15
      相关资源
      最近更新 更多