web-dev-qa-db-ja.com

サイトcoderbyteで「gets(stdin)」はどうなっていますか?

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への正しい入力値。

この特定のサイト以外では、コードは(予想どおり)コンパイルされません。そこで、ここで何が起こっていますか?


*実際には最初のものは_using namespace std_ですが、それはここでの私の問題とは無関係です。

143
bolov

私は興味をそそられます。したがって、調査用ゴーグルを装着する時間であり、コンパイラーまたはコンパイルフラグにアクセスできないため、独創性を発揮する必要があります。また、このコードについては何も理にかなっていないので、すべての仮定について悪い考えの質問ではありません。

まず、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を使用しない」 ...代わりに」これは確かに良いアドバイスですが、stdinからの有効な読み取りの試みはこのサイトで失敗するため、OPをより混乱させるだけです。


TLDR

gets(stdin)は無効なC++です。これはこの特定のサイトが使用する仕掛けです(理由はわかりませんが)。サイトで引き続き送信したい場合(私はそれを支持も支持もしません)、そうでなければ意味をなさないが、それがもろいことに注意してください。 mainをほとんど変更すると、エラーが発生します。このサイトの外部では、通常の入力読み取り方法を使用します。

112
bolov

私はCoderbyteの創設者であり、このgets(stdin)ハックを作成した人でもあります。

この投稿へのコメントは、それが検索と置換の形式であることは正しいので、なぜこれを本当に迅速に行ったのかを説明させてください。

サイトを最初に作成した日(2012年頃)に戻って、それはJavaScriptのみをサポートしていました。ブラウザで実行されているJavaScriptで「入力を読み込む」方法がなかったため、関数foo(input)があり、Node.jsからreadline()関数を使用して呼び出しましたfoo(readline())など。私が子供で、よく知らなかったのを除いて、私は文字通りreadline()を実行時の入力に置き換えました。したがって、foo(readline())foo(2)またはfoo("hello")になり、JavaScriptで正常に機能しました。

2013/2014年頃にさらに言語を追加し、サードパーティサービスを使用してコードをオンラインで評価しましたが、使用しているサービスでstdin/stdoutを実行することは非常に困難であったため、言語の同じ愚かな検索と置換に固執しましたPython、Ruby、最終的にはC++、C#などのように。

早速、私は自分のコンテナーでコードを実行しますが、奇妙なハックに慣れているため、stdin/stdoutの動作を更新しませんでした(回避方法を説明するフォーラムに投稿している人もいます)。

私はそれがベストプラクティスではなく、新しい言語を学習している人がこのようなハックを見るのは役に立たないことを知っていますが、アイデアは新しいプログラマーが入力をまったく読むことを心配せず、単にアルゴリズムを書くことに集中することでした問題。チャレンジサイトのコーディングに関する一般的な不満の1つは、新しいプログラマーがstdinからの読み取り方法やファイルからの行の読み取り方法を見つけるために多くの時間を費やすことでした。 。

デフォルトのコードと言語のstdin読み取りとともに、エディタページ全体をすぐに更新します。願わくば、C++プログラマーがCoderbyteをもっと楽しんでくれることを願っています:)

173
Daniel Borowski

Coderbyteエディターでmainに次の追加を試みました。

_std::cout << "gets(stdin)";
_

神秘的で不可解なスニペットgets(stdin)が文字列リテラル内に表示される場所。これは、プリプロセッサでさえ、何によっても変換されるべきではありません。anyC++プログラマーは、このコードが正確な文字列gets(stdin)標準出力に。それでも、coderbyteでコンパイルして実行すると、次の出力が表示されます。

_8
_

ここで、値_8_は、エディターの下の便利な「入力」フィールドから直接取得されます。

Magic code

このことから、このオンラインエディターがソースコードに対してブラインド検索および置換操作を実行し、gets(stdin)の外観をユーザーの「入力」に置き換えていることは明らかです。私は個人的にこれを不注意なプリプロセッサマクロよりも悪い言語の誤用と呼びます。

オンラインコーディングチャレンジWebサイトのコンテキストでは、型にはまらない、非標準、無意味で、少なくともunsafeプラクティスを教えるため、私はこれに心配していますgets(stdin)のように、他のプラットフォームでは繰り返せない方法で。

_std::cin_を使用して、プログラムに入力をストリームするだけでは(this難しいことはないはずです。

65
alter igel