it-swarm.com.de

Was ist los mit 'bekommt (stdin)' auf der Seite coderbyte?

Coderbyte ist eine Online-Codierungs-Challenge-Site (ich habe sie erst vor 2 Minuten gefunden).

Die erste C++ - Herausforderung Sie werden mit einem C++ - Skelett begrüßt, das Sie ändern müssen:

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

}

Wenn Sie mit C++ als erstes wenig vertraut sind* * das knallt in deinen Augen ist:

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

Also, ok, der Code ruft gets auf, was seit C++ 11 veraltet und seit C++ 14 entfernt ist, was an sich schlecht ist.

Aber dann merke ich: gets ist vom Typ char*(char*). Es sollte also keinen FILE* - Parameter akzeptieren und das Ergebnis sollte nicht anstelle eines int -Parameters verwendet werden können, sondern ... es wird nicht nur ohne Warnungen oder Fehler kompiliert, sondern Es wird ausgeführt und übergibt den korrekten Eingabewert an FirstFactorial.

Außerhalb dieser speziellen Site wird der Code nicht (wie erwartet) kompiliert. Was ist hier also los?


* Eigentlich ist der erste using namespace std, Aber das ist für mein Problem hier irrelevant.

143
bolov

Ich bin fasziniert. Also, Zeit, die Untersuchungsbrille aufzusetzen, und da ich keinen Zugriff auf den Compiler oder die Kompilierungsflags habe, muss ich erfinderisch werden. Auch weil nichts an diesem Code Sinn macht, ist es keine schlechte Idee, jede Annahme in Frage zu stellen.

Lassen Sie uns zuerst den tatsächlichen Typ von gets überprüfen. Ich habe einen kleinen Trick dafür:

template <class> struct Name;

int main() { 

    Name<decltype(gets)> n;

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

}

Und das sieht ... normal aus:

/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 ist als veraltet markiert und hat die Signatur char *(char *). Aber wie kompiliert FirstFactorial(gets(stdin));?

Versuchen wir etwas anderes:

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

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

} 

Was uns gibt:

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

Endlich bekommen wir etwas: decltype(8). Daher wurde die gesamte gets(stdin) textuell durch die Eingabe ersetzt (8).

Und die Dinge werden seltsamer. Der Compilerfehler wird fortgesetzt:

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

Jetzt erhalten wir den erwarteten Fehler für cout << FirstFactorial(gets(stdin));

Ich habe nach einem Makro gesucht und da #undef gets Nichts zu tun scheint, sieht es so aus, als wäre es kein Makro.

Aber

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

Es kompiliert.

Aber

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

Nicht mit dem erwarteten Fehler in der Zeile n2.

Und wieder bewirkt fast jede Änderung an main, dass die Zeile cout << FirstFactorial(gets(stdin)); den erwarteten Fehler ausspuckt.

Außerdem scheint das stdin tatsächlich leer zu sein.

Ich kann also nur schließen und spekulieren, dass sie ein kleines Programm haben, das die Quelle analysiert und (schlecht) versucht, gets(stdin) durch den Testfall-Eingabewert zu ersetzen, bevor es tatsächlich in den Compiler eingespeist wird. Wenn jemand eine bessere Theorie hat oder tatsächlich weiß, was er tut, teilen Sie dies bitte mit!

Dies ist offensichtlich eine sehr schlechte Praxis. Als ich dies recherchierte, stellte ich fest, dass es hier zumindest eine Frage gibt ( Beispiel ), und da die Leute keine Ahnung haben, dass es da draußen eine Site gibt, die dies tut, lautet ihre Antwort "benutze nicht gets use ... anstatt ", was in der Tat ein guter Rat ist, aber das OP nur mehr verwirrt, da jeder Versuch eines gültigen Lesens von stdin auf dieser Site fehlschlägt.


TLDR

gets(stdin) ist ungültig C++. Es ist eine Spielerei, die diese spezielle Site verwendet (aus welchen Gründen ich es nicht herausfinden kann). Wenn Sie weiterhin auf der Website einreichen möchten (ich unterstütze sie weder noch unterstütze ich sie), müssen Sie dieses Konstrukt verwenden, das sonst keinen Sinn ergibt, aber Sie müssen sich bewusst sein, dass es spröde ist. Fast alle Änderungen an main führen zu einem Fehler. Verwenden Sie außerhalb dieser Site normale Lesemethoden für Eingaben.

112
bolov

Ich bin der Gründer von Coderbyte und auch der Typ, der diesen gets(stdin) Hack erstellt hat.

Die Kommentare zu diesem Beitrag sind richtig, da es sich um eine Form des Findens und Ersetzens handelt. Lassen Sie mich daher erklären, warum ich dies sehr schnell getan habe.

Damals, als ich die Site zum ersten Mal erstellte (um 2012), unterstützte sie nur JavaScript. Es gab keine Möglichkeit, Eingaben in JavaScript, das im Browser ausgeführt wird, "einzulesen", und daher gab es eine Funktion foo(input), und ich habe die Funktion readline() von Node.js verwendet, um sie aufzurufen wie foo(readline()). Außer ich war ein Kind und wusste es nicht besser, also habe ich readline() buchstäblich zur Laufzeit durch die Eingabe ersetzt. So wurde foo(readline()) zu foo(2) oder foo("hello"), was für JavaScript gut funktionierte.

Um 2013/2014 habe ich weitere Sprachen hinzugefügt und Dienste von Drittanbietern verwendet, um Code online auszuwerten. Es war jedoch sehr schwierig, mit den von mir verwendeten Diensten stdin/stdout durchzuführen. Daher habe ich mich an das gleiche dumme Suchen und Ersetzen für Sprachen gehalten wie Python, Ruby und schließlich C++, C # usw.

Schneller Vorlauf bis heute, ich führe den Code in meinen eigenen Containern aus, habe aber nie die Funktionsweise von stdin/stdout aktualisiert, weil sich die Leute an den seltsamen Hack gewöhnt haben (einige Leute haben sogar in Foren gepostet, in denen erklärt wird, wie man ihn umgeht).

Ich weiß, dass es keine bewährte Methode ist und es für jemanden, der eine neue Sprache lernt, nicht hilfreich ist, solche Hacks zu sehen, aber die Idee war, dass neue Programmierer sich überhaupt keine Gedanken über das Lesen von Eingaben machen und sich nur darauf konzentrieren, den Algorithmus zu schreiben, um das Problem zu lösen Problem. Eine häufige Beschwerde über das Codieren von Challenge-Sites vor Jahren war, dass neue Programmierer viel Zeit damit verbringen würden, nur herauszufinden, wie man aus stdin liest oder Zeilen aus einer Datei liest. Deshalb wollte ich, dass neue Codierer dieses Problem auf Coderbyte vermeiden .

Ich werde in Kürze die gesamte Editorseite zusammen mit dem Standardcode und dem Lesen von stdin für Sprachen aktualisieren. Hoffentlich werden dann C++ - Programmierer Coderbyte mehr genießen :)

173
Daniel Borowski

Ich habe den folgenden Zusatz zu main im Coderbyte-Editor versucht:

std::cout << "gets(stdin)";

Wo das mysteriöse und rätselhafte Snippet gets(stdin) in einem String-Literal erscheint. Dies sollte möglicherweise von nichts transformiert werden, nicht einmal vom Präprozessor, und jeder C++ - Programmierer sollte erwarten, dass dieser Code die genaue Zeichenfolge gets(stdin) zur Standardausgabe. Und doch sehen wir die folgende Ausgabe, wenn sie kompiliert und auf Coderbyte ausgeführt wird:

8

Wobei der Wert 8 Direkt aus dem praktischen Eingabefeld unter dem Editor übernommen wird.

(Magic code

Daraus ergibt sich, dass dieser Online-Editor blinde Such- und Ersetzungsvorgänge für den Quellcode ausführt und das Erscheinungsbild von gets(stdin) durch die 'Eingabe' des Benutzers ersetzt. Ich persönlich würde dies einen Missbrauch der Sprache nennen, der schlimmer ist als unachtsame Präprozessor-Makros.

Im Zusammenhang mit einer Online-Codierungs-Challenge-Website mache ich mir darüber Sorgen, weil sie unkonventionelle, nicht standardmäßige, bedeutungslose und zumindest unsichere Praktiken vermittelt wie gets(stdin) und auf eine Weise, die auf anderen Plattformen nicht wiederholt werden kann.

Ich bin sicher, es kann nicht so schwer sein, nur std::cin Zu verwenden und Eingaben in ein Programm zu streamen.

65
alter igel