提问人:bolov 提问时间:3/21/2019 最后编辑:Peter Mortensenbolov 更新时间:3/28/2019 访问量:9267
站点 coderbyte 上的“gets(stdin)”是怎么回事?
What is going on with 'gets(stdin)' on the site coderbyte?
问:
Coderbyte 是一个在线编码挑战网站(我在 2 分钟前才找到它)。
迎接您的第一个 C++ 挑战有一个需要修改的 C++ 骨架:
#include <iostream> #include <string> using namespace std; int FirstFactorial(int num) { // Code goes here return num; } int main() { // Keep this function call here cout << FirstFactorial(gets(stdin)); return 0; }
如果您对 C++ 不太熟悉,那么首先映入眼帘的是:
int FirstFactorial(int num);
cout << FirstFactorial(gets(stdin));
所以,好的,代码调用自 C++11 以来已弃用,自 C++14 以来已删除,这本身就很糟糕。gets
但后来我意识到:是类型.所以它不应该接受参数,结果不应该代替参数,但是......它不仅在没有任何警告或错误的情况下进行编译,而且运行并实际将正确的输入值传递给 。gets
char*(char*)
FILE*
int
FirstFactorial
在这个特定站点之外,代码不会编译(如预期的那样),那么这里发生了什么?
*实际上第一个是使用命名空间 std
,但这与我在这里的问题无关。
答:
我很感兴趣。所以,是时候戴上调查护目镜了,因为我无法访问编译器或编译标志,我需要发挥创造力。此外,由于此代码没有任何意义,因此质疑每个假设都不是一个坏主意。
首先,我们来检查一下 的实际类型。我有一个小窍门:gets
template <class> struct Name;
int main() {
Name<decltype(gets)> n;
// keep this function call here
cout << FirstFactorial(gets(stdin));
return 0;
}
看起来......正常:
/tmp/613814454/Main.cpp:16:19: warning: 'gets' is deprecated [-Wdeprecated-declarations] Name<decltype(gets)> n; ^ /usr/include/stdio.h:638:37: note: 'gets' has been explicitly marked deprecated here extern char *gets (char *__s) __wur __attribute_deprecated__; ^ /usr/include/x86_64-linux-gnu/sys/cdefs.h:254:51: note: expanded from macro '__attribute_deprecated__' # define __attribute_deprecated__ __attribute__ ((__deprecated__)) ^ /tmp/613814454/Main.cpp:16:26: error: implicit instantiation of undefined template 'Name<char *(char *)>' Name<decltype(gets)> n; ^ /tmp/613814454/Main.cpp:12:25: note: template is declared here template <class> struct Name; ^ 1 warning and 1 error generated.
gets
标记为已弃用,并具有签名 。但是,编译是如何的呢?char *(char *)
FirstFactorial(gets(stdin));
让我们尝试其他方法:
int main() {
Name<decltype(gets(stdin))> n;
// keep this function call here
cout << FirstFactorial(gets(stdin));
return 0;
}
这给了我们:
/tmp/286775780/Main.cpp:15:21: error: implicit instantiation of undefined template 'Name<int>' Name<decltype(8)> n; ^
最后,我们得到了一些东西:.因此,整个在文本上被替换为输入()。decltype(8)
gets(stdin)
8
事情变得更奇怪了。编译器错误继续:
/tmp/596773533/Main.cpp:18:26: error: no matching function for call to 'gets' cout << FirstFactorial(gets(stdin)); ^~~~ /usr/include/stdio.h:638:14: note: candidate function not viable: no known conversion from 'struct _IO_FILE *' to 'char *' for 1st argument extern char *gets (char *__s) __wur __attribute_deprecated__;
所以现在我们得到了预期的错误cout << FirstFactorial(gets(stdin));
我检查了一个宏,由于似乎什么也没做,所以看起来它不是宏。#undef gets
但
std::integral_constant<int, gets(stdin)> n;
它编译。
但
std::integral_constant<int, gets(stdin)> n; // OK
std::integral_constant<int, gets(stdin)> n2; // ERROR wtf??
行中没有预期的错误。n2
同样,几乎任何使行的修改都会吐出预期的错误。main
cout << FirstFactorial(gets(stdin));
而且,实际上似乎是空的。stdin
因此,我只能得出结论并推测他们有一个小程序来解析源代码并尝试(糟糕地)在实际将其输入编译器输入之前替换为测试用例输入值。如果有人有更好的理论或真正知道他们在做什么,请分享!gets(stdin)
这显然是一种非常糟糕的做法。在研究这个问题时,我发现这里至少有一个关于这个问题的问题(示例),因为人们不知道有一个网站在那里这样做,他们的答案是“不要使用,使用......取而代之的是“这确实是一个很好的建议,但只会使 OP 更加困惑,因为任何从 stdin 有效读取的尝试都会在这个站点上失败。gets
顶级域名
gets(stdin)
无效的 C++。这是这个特定网站使用的噱头(出于什么原因我无法弄清楚)。如果你想继续在网站上提交(我既不认可它,也不认可它),你必须使用这个结构,否则就没有意义,但要注意它是脆弱的。几乎任何修改都会吐出错误。在本网站之外,请使用正常的输入读取方法。main
评论
std::cout << "gets(stdin)";
8
"gets(stdin)"
我在Coderbyte编辑器中尝试了以下添加:main
std::cout << "gets(stdin)";
神秘而神秘的片段出现在字符串文字中。这不应该被任何东西转换,甚至预处理器也不行,任何 C++ 程序员都应该期望此代码将确切的字符串打印到标准输出。然而,当在coderbyte上编译和运行时,我们会看到以下输出:gets(stdin)
gets(stdin)
8
该值直接从编辑器下方便的“输入”字段中获取。8
由此可见,这个在线编辑器正在对源代码执行盲目的查找和替换操作,即用用户的“输入”替换外观。我个人认为这是对语言的误用,这比粗心的预处理器宏更糟糕。gets(stdin)
在在线编码挑战网站的背景下,我对此感到担忧,因为它教授非常规、非标准、无意义且至少是不安全的做法,例如,并且以其他平台上无法重复的方式进行。gets(stdin)
我敢肯定,仅仅使用和将输入流式传输到程序不会这么难。std::cin
评论
gets(stdin)
System.out.print(FirstFactorial(s.nextLine()9));
89
s
我是 Coderbyte 的创始人,也是创建这个 hack 的人。gets(stdin)
这篇文章的评论是正确的,它是一种查找和替换的形式,所以让我解释一下为什么我很快就这样做了。
当我第一次创建这个网站时(大约在2012年),它只支持JavaScript。在浏览器中运行的 JavaScript 中没有办法“读入输入”,因此会有一个函数,我使用 Node.js 中的函数来调用它,就像 .除了我还是个孩子,不知道更好,所以我实际上只是在运行时替换了输入。所以成为或对 JavaScript 工作得很好。foo(input)
readline()
foo(readline())
readline()
foo(readline())
foo(2)
foo("hello")
大约在 2013/2014 年,我添加了更多语言并使用第三方服务在线评估代码,但是使用我正在使用的服务执行 stdin/stdout 非常困难,所以我坚持对 Python、Ruby 等语言使用同样愚蠢的查找和替换,最终使用 C++、C# 等。
快进到今天,我在自己的容器中运行代码,但从未更新 stdin/stdout 的工作方式,因为人们已经习惯了这种奇怪的黑客攻击(有些人甚至在论坛上发帖解释如何绕过它)。
我知道这不是最佳实践,对于学习一门新语言的人来说,看到这样的技巧是没有帮助的,但这个想法是让新程序员根本不担心读取输入,而只是专注于编写算法来解决问题。几年前,关于编码挑战网站的一个常见抱怨是,新程序员会花费大量时间来弄清楚如何从文件中读取或读取行,所以我希望新的程序员在 Coderbyte 上避免这个问题。stdin
我将很快更新整个编辑器页面以及默认代码和语言阅读。希望C++程序员会更:)地喜欢使用 Coderbytestdin
评论
TAKE_INPUT
#define TAKE_INPUT whatever_here
评论
stdin
FILE*
char*
gets()
gets(stdin )