理解一个不常见的主要参数

以下问题是在大学编程比赛中提出的。 我们被要求猜测输出和/或解释其工作。 不用说,我们没有人成功。

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