站点编码字节上的“ gets(stdin)”怎么回事?

bol*_*lov 144 c++ gets input standards-compliance

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;

}
Run Code Online (Sandbox Code Playgroud)

如果你稍微熟悉C ++的第一件事*在你的眼睛持久性有机污染物是:

int FirstFactorial(int num);
cout << FirstFactorial(gets(stdin));
Run Code Online (Sandbox Code Playgroud)

因此,好的,代码调用gets从C ++ 11开始不推荐使用,而从C ++ 14开始删除,这本身就是不好的。

但后来我意识到:gets是类型char*(char*)。因此,它不应该接受FILE*参数,并且结果不能代替int参数使用,但是...不仅可以编译时没有任何警告或错误,而且可以运行并将实际的正确输入值传递给FirstFactorial

在此特定站点之外,代码无法编译(如预期的那样),那么这里发生了什么?


*实际上第一个是,using namespace std但这与我在这里的问题无关。

小智 172

我是Coderbyte的创始人,也是创建此gets(stdin)hack的人。

关于此帖子的评论是正确的,因为它是一种查找和替换的形式,所以让我解释一下为什么我真的很快做到了。

在我最初创建网站的那天(大约2012年),它仅支持JavaScript。无法在浏览器中运行的JavaScript中“读取输入”,因此会有一个函数foo(input),我使用readline()Node.js中的函数像这样调用它foo(readline())。除了我还很小的时候,我并不了解,所以从字面上看,我只是readline()在运行时替换了输入。因此foo(readline())成为JavaScript foo(2)foo("hello")对JavaScript正常运行。

在2013/2014年左右,我添加了更多的语言,并使用了第三方服务来在线评估代码,但是很难对正在使用的服务执行stdin / stdout,因此我坚持使用相同的语言傻瓜式查找和替换例如Python,Ruby,最后是C ++,C#等。

时至今日,我在自己的容器中运行代码,但从未更新stdin / stdout的工作方式,因为人们已经习惯了怪异的hack(有些人甚至在论坛上发布了解释如何解决它的方法)。

我知道这不是最佳实践,对于学习新语言的人来说,看到这样的黑客行为是无济于事的,但是这个想法是让新程序员完全不用担心阅读输入,而只是专注于编写算法来解决问题。问题。几年前,有关编码挑战站点的一个普遍抱怨是,新程序员会花大量时间来弄清楚如何stdin从文件中读取或读取文件中的行,因此我希望新的编码器避免在Coderbyte上出现此问题。

我将尽快更新整个编辑器页面以及默认代码并stdin阅读语言。希望那时C ++程序员会喜欢更多使用Coderbyte的:)

  • 我真的没想到我在发布此答案时会选择其他答案。感谢您以如此出色的方式证明我做错了。看到您的回答真的很高兴。 (25认同)
  • “但是,这个想法是让新程序员完全不用担心阅读输入,而只是专注于编写解决问题的算法”-并不是您想到的,而是编写类似于“真实的东西”的东西。 ”代码,只是在该位置放置一个组合函数名称或明显的占位符?真是好奇。 (20认同)
  • 我们需要以_“我是x的创始人,也是创建此的人” _开头的更多答案。 (16认同)
  • 很有意思!我建议,如果您想保持这种技巧,可以用`TAKE_INPUT`这样的函数替换函数调用,然后使用查找替换在顶部插入#define TAKE_INPUT what_here`。 (4认同)
  • @iheanyi没有人要求它是完美的。实际上,我坚信,几乎*任何*占位符都会比*看上去*对任何新手来说都是有效代码,但实际上并未编译的东西更好。 (2认同)

bol*_*lov 112

我很感兴趣 因此,是时候放下调查镜了,并且由于我无权访问编译器或编译标志,因此我需要发挥创造力。另外,由于此代码没有意义,因此对每个假设都提出质疑并不是一个坏主意。

首先,让我们检查的实际类型gets。我有一个小窍门:

template <class> struct Name;

int main() { 

    Name<decltype(gets)> n;

  // keep this function call here
  cout << FirstFactorial(gets(stdin));
  return 0;

}
Run Code Online (Sandbox Code Playgroud)

看起来...正常:

/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.
Run Code Online (Sandbox Code Playgroud)

gets被标记为已弃用并具有签名char *(char *)。但是那怎么FirstFactorial(gets(stdin));编译呢?

让我们尝试其他方法:

int main() { 
  Name<decltype(gets(stdin))> n;

  // keep this function call here
  cout << FirstFactorial(gets(stdin));
  return 0;

} 
Run Code Online (Sandbox Code Playgroud)

这给了我们:

/tmp/286775780/Main.cpp:15:21: error: implicit instantiation of undefined template 'Name<int>'
  Name<decltype(8)> n;
                    ^
Run Code Online (Sandbox Code Playgroud)

最后,我们得到了一些东西: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__;
Run Code Online (Sandbox Code Playgroud)

所以现在我们得到了预期的错误 cout << FirstFactorial(gets(stdin));

我检查了一个宏,因为#undef gets似乎什么也没做,所以看起来它不是宏。

std::integral_constant<int, gets(stdin)> n;
Run Code Online (Sandbox Code Playgroud)

它编译。

std::integral_constant<int, gets(stdin)> n;    // OK
std::integral_constant<int, gets(stdin)> n2;   // ERROR                                          wtf??
Run Code Online (Sandbox Code Playgroud)

与该n2行的预期错误不符。

再一次,几乎任何修改main都会使该行cout << FirstFactorial(gets(stdin));吐出预期的错误。

而且stdin实际上似乎是空的。

因此,我只能得出结论,并推测他们有一个小程序可以解析源,并gets(stdin)在实际将其输入编译器之前尝试(较差)替换为测试用例的输入值。如果有人有更好的理论或实际上知道他们在做什么,请分享!

这显然是非常糟糕的做法。在研究这个问题时,我发现这里至少有一个问题(示例),因为人们不知道那里有一个站点可以这样做,所以他们的回答是“不要使用getsuse ...代替”,这的确是一个很好的建议,但只会使OP更加混乱,因为任何从stdin进行有效读取的尝试都将在此站点上失败。


TLDR

gets(stdin)无效的C ++。这个特定网站使用的是一个头(出于什么原因我无法弄清楚)。如果要继续在网站上提交(我既不认可也不不认可),则必须使用此结构,否则该结构将无济于事,但请注意它很脆弱。几乎所有对的修改main都会吐出一个错误。在此站点之外,请使用常规的输入阅读方法。

  • 我真的很惊讶。也许这个Q / A可以成为关于为什么不向挑战网站编码学习的经典文章。 (27认同)
  • 确实发生了一些邪恶的事情,我认为这是在编译器之外的源代码中的文本替换级别。尝试以下操作:`std :: cout &lt;&lt;“ gets(stdin)”;`,输出为`8`(或在“输入”字段中键入的任何内容。这是对语言的可耻滥用。 (27认同)
  • @Stobor注意`“ gets(stdin)”`周围的引号。这是一个字符串文字,即使预处理器也不会碰到 (13认同)
  • 引用詹姆斯·柯克的话:“这真该死。” (2认同)
  • @alterigel下车。这不是从编码质询站点进行学习是否有用的声明。您是谁来决定人们如何练习事物? (2认同)

alt*_*gel 64

main在Coderbyte编辑器中尝试了以下功能:

std::cout << "gets(stdin)";
Run Code Online (Sandbox Code Playgroud)

gets(stdin)字符串文字内出现神秘神秘的代码段的位置。这不应该被任何东西(甚至是预处理器)进行转换,任何 C ++程序员都应该期望此代码将确切的字符串打印gets(stdin)到标准输出中。但是,当编译并在coderbyte上运行时,我们看到以下输出:

8
Run Code Online (Sandbox Code Playgroud)

8从编辑器下方便的“输入”字段直接获取值的位置。

魔术码

由此可见,该在线编辑器正在对源代码执行盲目查找和替换操作,gets(stdin)用用户的“输入” 代替外观。我个人认为这是对语言的滥用,这比粗心的预处理器宏还要糟糕。

在一个在线编码挑战网站的上下文中,我对此感到担心,因为它会讲授非常规,非标准,无意义且至少是不安全的做法(例如)gets(stdin),并且这种方式在其他平台上无法重复。

我敢肯定,这不可能是这个很难只使用std::cin,只是流输入到程序。

  • @bolov可能只是被替换的gets(stdin)的第一次出现?我的意思是“盲目的”,因为它似乎并不了解该语言的语法或语法。 (4认同)
  • @Stobor dang,您是对的。我也可以确认这种情况也发生在Java中,即使未定义s,行System.out.print(FirstFactorial(s.nextLine()9));也会打印89。 (3认同)