繁体   English   中英

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

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

我见过一个C程序,它在不使用任何循环结构或条件语句的情况下可以打印1到1000,但是我不知道它是如何工作的。 任何人都可以遍历代码并解释每一行吗? 在这里观看现场演示。 为什么会导致运行时错误?

#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函数中删除返回类型。

打破您要求的界限

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

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

[i / 1000]

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

(i + 1);

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

这里有一堆未定义的行为。 根据标准,将main声明为具有单个int参数是非法的,尽管通常可以将命令行参数的数量作为单个参数来工作。 获取指向main的指针同样是未定义的,因为main是一个特殊的函数,可能具有非标准的调用约定。

这段代码很好地说明了可以弯曲C而不破坏它的程度。 在编写可理解的 C语言之前,您不应该尝试理解这一点。

以下是主要功能和假设:

首先,假设调用该程序时不带参数。 参数i通常位于argc ,因此它将具有初始值1(参数数组中的元素数,通常称为argv )。

接下来,假设main的单参数形式不会引起任何问题。 正式支持的main形式不带参数,2个参数( argc, argv ),有时还包括3个参数( argc, argv, envp )。

因此,第一个printf打印1。

接下来,我们有一个复合文字。 如果您先看一个简单的书,可能会更容易理解:

(int[]){10,20}

这是2个int的匿名数组,值分别为10和int[]是类型。 它在括号内的值列表之前。 那是什么

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

void(*[])()是一种类型。 这意味着带有签名void (*foo)()的函数指针数组。 括号中的类型名称后跟括号列表是复合文字,就像(int[]){10,20}示例一样。 在这种情况下,它将创建一个由2个函数指针组成的数组,其元素为mainexit

假设函数指针类型(返回void )和main函数(返回int )之间的不匹配不会导致问题。

这个:

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

是我们的2个元素的匿名数组,在一些多余的括号内,后跟[i / 1000] 那只是普通的数组索引语法。 如果i/1000为0,则获得数组的第一个元素( thearray[0] ),即main 对于所有i在0到999之间的情况,都会发生这种情况。如果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)

main任何调用都不会返回,并且return 0永远不会发生,因为exit终止该过程。 该进程返回退出代码1001而不是0的事实似乎是ideone的“运行时错误”消息的原因。

main function是递归调用的。

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

就像Sourav Ghosh所说的那样,禁止与main()递归使用,最好使用另一个函数。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM