問題描述
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 起被刪除,這本身就很糟糕.
但后來我意識到:gets
是 char*(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;
它編譯.?/p>
但是
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.
這篇關于站點 coderbyte 上的“gets(stdin)"是怎么回事?的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,也希望大家多多支持html5模板網!