理解一个不常见的主要参数
以下问题是在大学编程比赛中提出的。 我们被要求猜测输出和/或解释其工作。 不用说,我们没有人成功。
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类型,返回值始终存储在相同的寄存器中(例如,x86上的EAX)。 因此,如果你省略了return
语句,返回值很可能是最近返回的函数。
int main(int argc) {
int result = write( read(0, &argc, 1) && main() );
return result;
}
read
的调用或多或少是明显的:它从(文件描述符0)中的标准读入到位于&argc
的存储器中1
个字节。 如果读取成功,则返回1
,否则返回0。
&&
是逻辑“和”运算符。 当且仅当它的左手边是“真”(技术上,任何非零值)时,它评估它的右边。 &&
表达式的结果是一个总是1(对于“真”)或0(对于假)的int
。
在这种情况下,右侧调用没有参数的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 <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的变量。 它用作临时存储。
程序将尝试从标准输入中读取一个字节。 如果有输入,它会调用main递归地继续读取一个字节。
在输入结束时, read(2)
将返回0,表达式将返回0, write(2)
系统调用将执行,并且调用链可能会放开。
我在这里说“可能”,因为从这一点来看,结果是高度依赖于实施的。 其他write(2)
参数write(2)
缺失,但寄存器和堆栈中的内容将被传入内核。 同样未定义的行为适用于来自main
的各种递归激活的返回值。
在我的x86_64 Mac上,程序读取标准输入直到EOF,然后退出,根本不写任何东西。
链接地址: http://www.djcxy.com/p/73591.html上一篇: Understanding an uncommon argument to main
下一篇: C program for GCC and MS Visual Express C++ works only for GCC