这段打印 1 到 1000 的代码在 C 中如何工作?

How does this code that prints 1 to 1000 work in C?

我见过一个 C 程序打印 1 到 1000 而不使用任何循环结构或条件语句,但我不明白它是如何工作的。任何人都可以通过代码并解释每一行吗? 查看现场演示 here。为什么会导致运行时错误?

#include <stdio.h>
#include <stdlib.h>
int main(int i)
{
    printf("%d\n",i);
    ((void(*[])()){main, exit})[i / 1000](i + 1);
    return 0;
}
((void(*[])()){main, exit})[i / 1000](i + 1);

此行创建一个函数指针的二元数组,第一个元素包含 main 函数,另一个元素包含 exit 函数。

然后它通过 i / 1000 索引这个数组。这要么在 i < 1000 时获得 main 函数,要么在 i == 1000.

时获得 exit 函数

然后它调用它刚刚用 i+1 作为参数索引的函数指针。

这是一个递归函数,停止条件由数组索引而不是条件确定。但是我认为它不是有效的 C; main 的签名错误,转换为函数指针会从 main 函数中删除 return 类型。

分解您询问的行

((void(*[])()){main, exit})

这是一个数组文字,它创建一个包含两个函数指针的未命名临时数组,并将这些指针初始化为指向 mainexit

[i / 1000]

这会索引该临时数组。整数除法向 0 截断,因此当 0 <= i < 1000 时,它获取元素 0(指向 main 的指针),当 1000 <= i < 1999 时,它获取元素 1(指向 [=14= 的指针) ])

(i + 1);

这将以 i+1 作为参数调用指向的函数。

这里有很多东西是未定义的行为。根据标准,将 main 声明为具有单个 int 参数是非法的,但通常会起作用,将命令行参数的数量作为单个参数。获取指向 main 的指针同样是未定义的,因为 main 是一个可能具有非标准调用约定的特殊函数。

递归调用main function

void print_nb(int n)
{
    if (n <= 1000)
    {
        printf("%d\n", n);
        print_nb(n + 1);
    }
}

正如 Sourav Ghosh 所说,禁止使用 with main() 递归,最好使用其他函数。

这段代码很好地说明了在不破坏 C 的情况下可以将其弯曲多远。在你可以写 understandable C.

之前,你不应该试图理解这一点

以下是主要特征和假设:

首先,假设程序是在没有参数的情况下调用的。参数i在通常称为argc的地方,因此它的初始值为1(参数数组中元素的个数,通常称为argv)。

接下来,假设 main 的单参数形式不会导致任何问题。 main 的官方支持形式是没有参数、2 个参数 (argc, argv),有时是 3 个参数 (argc, argv, envp)。

所以第一个 printf 打印 1.

接下来我们有一个复合文字。先看一个比较简单的可能会更容易理解:

(int[]){10,20}

这是一个 2 int 的匿名数组,值为 10 和 20。int[] 是类型。它放在括号值列表之前的括号中。那么这是什么:

(void(*[])()){main, exit}

void(*[])() 是一种类型。它表示具有签名 void (*foo)() 的函数指针数组。括号中的类型名称后跟花括号列表是复合文字,就像 (int[]){10,20} 示例一样。在本例中,它创建了一个包含 2 个函数指针的数组,其元素为 mainexit.

假设函数指针类型 (returns void) 和 main (returns int) 不匹配不会导致一个问题。

这个:

((void(*[])()){main, exit})[i / 1000]

是我们的 2 个元素的匿名数组,在一些多余的括号内,后跟 [i / 1000]。这只是普通的数组索引语法。如果 i/1000 为 0,则得到数组的第一个元素 (thearray[0]),即 main。对于所有介于 0 和 999 之间的 i,都会发生这种情况。如果 i/1000 为 1,这发生在 i==1000 时,我们正在查看 thearray[1],这会得到第二个元素: exit.

到目前为止,我们有一个表达式 taht 在 i<1000 时等于 main,在 i==1000 时等于 exit

现在看看整个声明:

...that_big_thing_that_is_either_main_or_exit...(i + 1)

一个函数指针,后跟一个带括号的参数列表。那是一个函数调用。无论我们从数组中选择哪个函数,现在我们都将调用它,提供一个等于传入参数 (i) 加 1 的参数。

所以第一次调用,当 i 为 1 时,为函数选择 main,为参数选择 i+1 = 1+1 = 2。它调用 main(2).

main(2) 执行 printf 打印 2,然后调用 main(3).

main(3) 执行 printf 打印 3,然后调用 main(4).

...等等直到...

main(999) 执行 printf 打印 999,然后调用 main(1000).

main(1000) 执行 printf 打印 1000,然后调用 exit(1001).

None 调用 main 曾经 return,而 return 0 从未发生过,因为 exit 终止了进程。进程 return 的退出代码为 1001 而不是 0 的事实似乎是 ideone 的 "runtime error" 消息的原因。