下面的问题在高校程序设计大赛给予。 我们被要求猜测输出和/或解释其工作。 不用说,我们都不成功。
main(_){write(read(0,&_,1)&&main());}
一些短的谷歌搜索使我这个确切的问题,在问codegolf.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,返回值总是被存储在相同的寄存器(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;
}
结论
许多计算机上如预期该程序将无法正常工作。 即使你使用同一台计算机作为原作者,它可能无法在不同的操作系统上运行。 即使你使用同一台计算机和相同的操作系统,它不会在很多编译工作。 即使你使用同一台计算机的编译器和操作系统,它可能不是如果你改变了编译器的命令行标志工作。
正如我在评论中说,这个问题并没有一个有效的答案。 如果你发现了一个比赛组织者或竞赛法官,说否则,不邀请他们到你的下一个比赛。
好, _
是刚刚在早期K&R C语法带有int默认类型声明的变量。 它用作临时存储。
该程序将尝试从标准输入读取一个字节。 如果有输入,它会调用主递归继续读一个字节。
在输入结束时, read(2)
将返回0,表达式将返回0时, write(2)
系统调用将执行,并调用链可能会放松。
我说“可能”在这里是因为从结果这一点是高度依赖于实现。 其他参数来write(2)
失踪的事 ,但会在寄存器和堆栈,所以事情会被传递到内核。 同样不确定的行为适用于从各个递归激活的返回值main
。
在我的Mac x86_64的,程序读取标准输入直到EOF,然后退出,写什么都没有。