理解main的一个不常见的参数

在大学编程竞赛中提出了以下问题。 我们被要求猜测输出和/或解释它的工作原理。 不用说,我们都没有成功。

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

一些简短的谷歌搜索引导我到这个确切的问题,在codegolf.stackexchange.comcodegolf.stackexchange.com

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

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

我也在这个问题上找到了一些帮助: 主要的三个参数,以及其他混淆的技巧,但它仍然没有解释main(_)&_&&main()工作。

我的问题是,这些语法如何工作? 它们是我应该知道的东西,因为它们仍然相关吗?

如果不是直接的答案,我会感激任何指针(资源链接等)。

这个程序做了什么?

 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类型,返回值始终存储在相同的寄存器中(例如,x86上的EAX)。 因此,如果省略return语句,则返回值可能是最近返回的函数。

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

read的调用或多或少是明显的:它从标准的(文件描述符0)读取到位于&argc的内存中1个字节。 如果读取成功则返回1 ,否则返回0。

&&是逻辑“和”运算符。 当且仅当它的左侧是“真”时(技术上,任何非零值),它评估其右侧。 &&表达式的结果是一个int ,它始终为1(对于“true”)或0(对于false)。

在这种情况下,右侧调用main而不带参数。 在用1参数声明它之后调用main没有参数是未定义的行为。 然而,只要您不关心argc参数的初始值,它通常可以工作。

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

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

嗯。 快速查看手册页可以看出, write有三个参数,而不是一个。 另一种未定义行为的情况。 就像使用太少的参数调用main一样,我们无法预测将为第二和第三个参数write什么。 在典型的计算机上,他们会得到一些东西 ,但我们无法确定是什么。 (在非典型计算机上,可能会发生奇怪的事情。)作者依赖于write接收先前存储在内存堆栈中的内容。 并且,他依靠是第二和第三个要阅读的论点。

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

修复对main的无效调用,添加标题,扩展&&我们有:

 #include  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; } 

结论

该程序在许多计算机上无法正常工作。 即使您使用与原始作者相同的计算机,它也可能无法在其他操作系统上运行。 即使您使用相同的计算机和相同的操作系统,它也无法在许多编译器上运行。 即使您使用相同的计算机编译器和操作系统,如果更改编译器的命令行标志,它也可能不起作用。

正如我在评论中所说,问题没有一个有效的答案。 如果您发现比赛组织者或比赛裁判另有说明,请不要邀请他们参加您的下一场比赛。

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

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

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

我在这里说“可能”,因为从这一点来看,结果是高度依赖于实现的。 缺少write(2)的其他参数,但是会在寄存器和堆栈中出现一些内容 ,因此会将某些内容传递给内核。 相同的未定义行为适用于main的各种递归激活的返回值。

在我的x86_64 Mac上,程序读取标准输入直到EOF,然后退出,根本不写任何内容。