在大学编程竞赛中提出了以下问题。 我们被要求猜测输出和/或解释它的工作原理。 不用说,我们都没有成功。
main(_){write(read(0,&_,1)&&main());}
一些简短的谷歌搜索引导我到这个确切的问题,在codegolf.stackexchange.com
中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。
&&
是逻辑“和”运算符。 当且仅当它的左侧是“真”时(技术上,任何非零值),它评估其右侧。 &&
表达式的结果是一个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,然后退出,根本不写任何内容。
需要了解更多c/c++开发分享理解main的一个不常见的参数,也可以关注C/ C++技术分享栏目—计算机技术网(www.ctvol.com)!
以上就是c/c++开发分享理解main的一个不常见的参数相关内容,想了解更多C/C++开发(异常处理)及C/C++游戏开发关注计算机技术网(www.ctvol.com)!)。
本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。
ctvol管理联系方式QQ:251552304
本文章地址:https://www.ctvol.com/c-cdevelopment/980266.html