这个程序是做什么的?
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)中读取1个字节,存储到
&argc
指向的内存中。如果读取成功,则返回
1
,否则返回
0
。
&&
是逻辑“与”运算符。当且仅当左侧为“true”(技术上任何非零值)时,它才会计算右侧表达式。
&&
表达式的结果是一个
int
类型,始终为1(表示“true”)或0(表示false)。
在这种情况下,右侧调用不带参数的
main
函数。在声明带有1个参数的
main
函数后不带参数调用
main
函数是未定义行为。尽管如此,只要您不关心
argc
参数的初始值,它通常可以正常工作。
&&
的结果然后传递给
write()
函数。因此,我们的代码现在看起来像这样:
int main(int argc) {
int read_result = read(0, &argc, 1) && main();
int result = write(read_result);
return result;
}
嗯。快速查看man页面显示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 <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;
}
结论
这个程序在许多计算机上不能按预期工作。即使您使用与原始作者相同的计算机,它在不同的操作系统上可能无法正常工作。即使您使用相同的计算机和操作系统,它也无法在许多编译器上工作。即使您使用相同的计算机编译器和操作系统,如果更改编译器的命令行标志,它也可能无法工作。
正如我在评论中所说,这个问题没有一个有效的答案。如果你找到了一个比赛组织者或比赛裁判说可以解决这个问题,请不要邀请他们参加你的下一个比赛。