1. ホーム
  2. c++

[解決済み] サイトcoderbyteの'gets(stdin)'はどうなっているのでしょうか?

2022-05-15 03:05:57

質問

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

ということで、よし、このコードでは gets を呼び出しますが、これは C++11 から非推奨となり、C++14 からは削除されたので、それ自体は悪いことです。

しかし、そこで気がついた。 getschar*(char*) . ですから FILE* パラメータを受け入れてはいけませんし、その結果は int パラメータの代わりに使えるはずがないのですが、、、 警告やエラーなしにコンパイルされるだけでなく、実行され、実際に正しい入力値が FirstFactorial .

この特定のサイト以外では、コードは(予想通り)コンパイルされませんが、ここで何が起こっているのでしょうか?


*実は1つ目は using namespace 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) をテストケースの入力値に置き換えてから、実際にコンパイラーに送り込んでいるのではないかと推測しています。誰かがより良い理論を持っているか、実際に彼らが何をしているかを知っているならば、共有してください。

これは明らかに非常に悪い習慣です。これを研究している間、私は少なくともここに質問があることを発見しました ( を使わないでください。 gets これは確かに良いアドバイスですが、このサイトでは標準入力から正しく読み込もうとすると失敗するので、OPをさらに混乱させるだけです。


TLDR

gets(stdin) は無効なC++です。これは、この特定のサイトが使用しているギミックです (どういう理由でそうなっているのか、私にはわかりません)。もしあなたがこのサイトで投稿を続けたいなら(私はそれを支持するわけでも支持しないわけでもありません)、さもなければ意味をなさないこの構造を使わなければなりませんが、これはもろいということに注意してください。ほとんどすべての main を変更すると、エラーが発生します。このサイト以外では、通常の入力読み取り方法を使用してください。