了解一种罕见的参数主要(Understanding an uncommon argument to

2019-07-29 03:57发布

下面的问题在高校程序设计大赛给予。 我们被要求猜测输出和/或解释其工作。 不用说,我们都不成功。

main(_){write(read(0,&_,1)&&main());}

一些短的谷歌搜索使我这个确切的问题,在问codegolf.stackexchange.com

https://codegolf.stackexchange.com/a/1336/4085

在那里,它解释它的作用: Reverse stdin and place on stdout ,而不是如何

我还发现,在这个问题有所帮助: 三个参数传递给主,和其他混淆技巧 ,但它仍然没有解释如何main(_) &_&&main()的作品。

我的问题是, 如何做这些语法工作? 他们是什么,我应该知道,作为,是他们还有用吗?

我将是任何指针(至资源链接等)感激,如果不是彻底的答案。

Answer 1:

这是什么程序呢?

main(_){write(read(0,&_,1)&&main());}

之前我们分析它,让我们来美化它:

main(_) {
    write ( read(0, &_, 1) && main() );
}

首先,你应该知道, _是有效的变量名,虽然难看。 让我们改变它:

main(argc) {
    write( read(0, &argc, 1) && main() );
}

接着,认识到返回类型的功能,并且一个参数的类型在C可选的(但不是在C ++):

int main(int argc) {
    write( read(0, &argc, 1) && main() );
}

接下来,了解如何返回值的工作。 对于某些类型的CPU,返回值总是被存储在相同的寄存器(EAX在x86,例如)。 因此,如果你省略了一个return语句,返回值可能会是任何最近的函数返回。

int main(int argc) {
    int result = write( read(0, &argc, 1) && main() );
    return result;
}

到呼叫read是更多或更少的明显的:它从标准(文件描述符0)读出,到位于存储器&argc ,为1字节。 它返回1如果读取成功,否则为0。

&&是逻辑“与”操作。 当且仅当它的左手侧是“真”(从技术上说,任何非零值)计算它的右手边。 所述的结果&&表达式是一个int这始终是1(用于“真”)或0(假)。

在这种情况下,右手边调用main不带任何参数。 调用main用1个参数声明之后不带参数是不确定的行为。 然而,经常的工作,只要你不关心的初始值argc参数。

该结果&&然后传递给write() 。 所以,我们的代码现在看起来像:

int main(int argc) {
    int read_result = read(0, &argc, 1) && main();
    int result = write(read_result);
    return result;
}

嗯。 快速浏览一下该男子页显示, write有三个参数,没有之一。 未定义行为的另一种情况。 就像调用main用过多的参数,我们无法预测什么write将收到的第2和第3的参数。 在典型的电脑,他们将得到的东西 ,但我们不能肯定知道。 (非典型电脑,奇怪的事情都可能发生。)作者是在靠write任何先前存储内存堆栈上接收。 而且,他在依靠作为第二和第三个参数的读取。

int main(int argc) {
    int read_result = read(0, &argc, 1) && main();
    int result = write(read_result, &argc, 1);
    return result;
}

固定到非法呼叫main ,并添加标题,并扩大&&我们有:

#include <unistd.h>
int main(int argc, int argv) {
    int result;
    result = read(0, &argc, 1);
    if(result) result = main(argc, argv);
    result = write(result, &argc, 1);
    return result;
}


结论

许多计算机上如预期该程序将无法正常工作。 即使你使用同一台计算机作为原作者,它可能无法在不同的操作系统上运行。 即使你使用同一台计算机和相同的操作系统,它不会在很多编译工作。 即使你使用同一台计算机的编译器和操作系统,它可能不是如果你改变了编译器的命令行标志工作。

正如我在评论中说,这个问题并没有一个有效的答案。 如果你发现了一个比赛组织者或竞赛法官,说否则,不邀请他们到你的下一个比赛。



Answer 2:

好, _是刚刚在早期K&R C语法带有int默认类型声明的变量。 它用作临时存储。

该程序将尝试从标准输入读取一个字节。 如果有输入,它会调用主递归继续读一个字节。

在输入结束时, read(2)将返回0,表达式将返回0时, write(2)系统调用将执行,并调用链可能会放松。

我说“可能”在这里是因为从结果这一点是高度依赖于实现。 其他参数来write(2)失踪的事 ,但会在寄存器和堆栈,所以事情会被传递到内核。 同样不确定的行为适用于从各个递归激活的返回值main

在我的Mac x86_64的,程序读取标准输入直到EOF,然后退出,写什么都没有。



文章来源: Understanding an uncommon argument to main