站点 coderbyte 上的“gets(stdin)"是怎么回事?

2021-12-21 00:00:00 input c++ standards-compliance gets

Coderbyte 是一个在线编程挑战网站(我在 2 分钟前发现它).

您遇到的第一个 C++ 挑战有一个 C++ 框架你需要修改:

<块引用>

#include #include <字符串>使用命名空间标准;int FirstFactorial(int num) {//代码在这里返回编号;}int main() {//保持这个函数调用在这里cout<<FirstFactorial(gets(stdin));返回0;}

如果您对 C++ 不太熟悉,首先* 会出现在您的眼前:

int FirstFactorial(int num);cout<<FirstFactorial(gets(stdin));

所以,好的,代码调用了 gets,它自 C++11 起已被弃用,自 C++14 起被删除,这本身就很糟糕.

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

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

<小时>

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

解决方案

我很好奇.所以,是时候戴上调查眼镜了,因为我无法访问编译器或编译标志,所以我需要发挥创造力.此外,因为这段代码没有任何意义,所以对每个假设提出质疑并不是一个坏主意.

首先让我们检查gets的实际类型.我有一个小技巧:

模板结构名称;int main() {名称n;//保持这个函数调用在这里cout<<FirstFactorial(gets(stdin));返回0;}

这看起来……很正常:

<块引用>

/tmp/613814454/Main.cpp:16:19: 警告:'gets' 已被弃用 [-Wdeprecated-declarations]名称n;^/usr/include/stdio.h:638:37: 注意:'gets' 已在此处明确标记为已弃用extern char *gets (char *__s) __wur __attribute_deprecated__;^/usr/include/x86_64-linux-gnu/sys/cdefs.h:254:51:注意:从宏__attribute_deprecated__"扩展# 定义 __attribute_deprecated__ __attribute__ ((__deprecated__))^/tmp/613814454/Main.cpp:16:26: 错误:未定义模板的隐式实例化 'Name'名称n;^/tmp/613814454/Main.cpp:12:25:注意:模板在此处声明模板<类>结构名称;^生成了 1 个警告和 1 个错误.

gets 被标记为已弃用并具有签名 char *(char *).但是,FirstFactorial(gets(stdin)); 是如何编译的?

让我们试试别的:

int main() {名称n;//保持这个函数调用在这里cout<<FirstFactorial(gets(stdin));返回0;}

这给了我们:

<块引用>

/tmp/286775780/Main.cpp:15:21: 错误:未定义模板 'Name' 的隐式实例化名称n;^

我们终于得到了一些东西:decltype(8).所以整个 gets(stdin) 被文本替换为输入 (8).

事情变得更奇怪了.编译器错误继续:

<块引用>

/tmp/596773533/Main.cpp:18:26: 错误:没有匹配的函数调用gets"cout<<FirstFactorial(gets(stdin));^~~~/usr/include/stdio.h:638:14:注意:候选函数不可行:第一个参数没有从struct _IO_FILE *"到char *"的已知转换extern char *gets (char *__s) __wur __attribute_deprecated__;

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

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

但是

std::integral_constantn;

它编译.

但是

std::integral_constantn;//行std::integral_constantn2;//错误 wtf??

n2 行没有出现预期的错误.

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

此外,stdin 实际上似乎是空的.

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

这显然是一种非常糟糕的做法.在研究这个时,我发现这里至少有一个问题(example) 关于这一点,因为人们不知道有一个网站在那里做这件事,他们的答案是不要使用 gets 而是使用 ...".这确实是一个很好的建议,但只会让 OP 更加困惑,因为任何从 stdin 进行有效读取的尝试都将在此站点上失败.


TLDR

gets(stdin) 是无效的 C++.这是这个特定网站使用的噱头(我不知道是什么原因).如果你想继续在网站上提交(我既不认可也不不认可它)你必须使用这个结构,否则就没有意义,但要注意它是脆弱的.几乎对 main 的任何修改都会抛出错误.在本站之外使用正常的输入阅读方法.

Coderbyte is an online coding challenge site (I found it just 2 minutes ago).

The first C++ challenge you are greeted with has a C++ skeleton you need to modify:

#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;

}

If you are little familiar with C++ the first thing* that pops in your eyes is:

int FirstFactorial(int num);
cout << FirstFactorial(gets(stdin));

So, ok, the code calls gets which is deprecated since C++11 and removed since C++14 which is bad in itself.

But then I realize: gets is of type char*(char*). So it shouldn't accept a FILE* parameter and the result shouldn't be usable in the place of an int parameter, but ... not only it compiles without any warnings or errors, but it runs and actually passes the correct input value to FirstFactorial.

Outside of this particular site, the code doesn't compile (as expected), so what is going on here?


*Actually the first one is using namespace std but that is irrelevant to my issue here.

解决方案

I am intrigued. So, time to put the investigation goggles on and since I don't have access to the compiler or compilation flags I need to get inventive. Also because nothing about this code makes sense it's not a bad idea question every assumption.

First let's check the actual type of gets. I have a little trick for that:

template <class> struct Name;

int main() { 
    
    Name<decltype(gets)> n;
  
  // keep this function call here
  cout << FirstFactorial(gets(stdin));
  return 0;
    
}

And that looks ... normal:

/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 is marked as deprecated and has the signature char *(char *). But then how is FirstFactorial(gets(stdin)); compiling?

Let's try something else:

int main() { 
  Name<decltype(gets(stdin))> n;
  
  // keep this function call here
  cout << FirstFactorial(gets(stdin));
  return 0;
    
} 

Which gives us:

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

Finally we are getting something: decltype(8). So the entire gets(stdin) was textually replaced with the input (8).

And the things get weirder. The compiler error continues:

/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__;

So now we get the expected error for cout << FirstFactorial(gets(stdin));

I checked for a macro and since #undef gets seems to do nothing it looks like it isn't a macro.

But

std::integral_constant<int, gets(stdin)> n;

It compiles.

But

std::integral_constant<int, gets(stdin)> n;    // OK
std::integral_constant<int, gets(stdin)> n2;   // ERROR                                          wtf??

Doesn't with the expected error at the n2 line.

And again, almost any modification to main makes the line cout << FirstFactorial(gets(stdin)); spit out the expected error.

Moreover the stdin actually seems to be empty.

So I can only conclude and speculate they have a little program that parses the source and tries (poorly) to replace gets(stdin) with the test case input value before actually feeding it into the compiler. If anybody has a better theory or actually knows what they are doing please share!

This is obviously a very bad practice. While researching this I found there is at least a question here (example) about this and because people have no idea that there is a site out there who does this their answer is "don't use gets use ... instead" which is indeed a good advice but only confuses the OP more since any attempt at a valid read from stdin will fail on this site.


TLDR

gets(stdin) is invalid C++. It's a gimmick this particular site uses (for what reasons I cannot figure out). If you want to continue to submit on the site (I am neither endorsing it neither not endorsing it) you have to use this construct that otherwise would not make sense, but be aware that it is brittle. Almost any modifications to main will spit out an error. Outside of this site use normal input reading methods.

相关文章