Coderbyte to internetowa strona z wyzwaniami z kodowania (znalazłem ją zaledwie 2 minuty temu).
Pierwsze wyzwanie C ++, z którym się witasz, zawiera szkielet C ++, który musisz zmodyfikować:
#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; }
Jeśli słabo znasz C ++, pierwszą rzeczą, która * pojawia się w twoich oczach, jest:
int FirstFactorial(int num);
cout << FirstFactorial(gets(stdin));
Więc ok, kod wywołuje, gets
który jest przestarzały od C ++ 11 i usunięty od C ++ 14, który sam w sobie jest zły.
Ale potem zdaję sobie sprawę: gets
jest typowy char*(char*)
. Dlatego nie powinien akceptować FILE*
parametru, a wynik nie powinien być użyteczny zamiast int
parametru, ale ... nie tylko kompiluje się bez żadnych ostrzeżeń i błędów, ale działa i faktycznie przekazuje poprawną wartość wejściową do FirstFactorial
.
Poza tą konkretną witryną kod nie kompiluje się (zgodnie z oczekiwaniami), więc co się tutaj dzieje?
* Właściwie pierwszy z nich jest taki, using namespace std
ale nie ma to związku z moim problemem.
źródło
stdin
w standardowej bibliotece jest aFILE*
, a wskaźnik do dowolnego typu jest konwertowany nachar*
, który jest typem argumentugets()
. Jednak nigdy, przenigdy nie powinieneś pisać tego rodzaju kodu poza zaciemnionym konkursem w C. Jeśli Twój kompilator w ogóle to akceptuje, dodaj więcej flag ostrzegawczych, a jeśli próbujesz naprawić bazę kodu zawierającą tę konstrukcję, zamień ostrzeżenia w błędy.gets(stdin )
(z dodatkową spacją) powoduje oczekiwany błąd C ++.Odpowiedzi:
Jestem założycielem Coderbyte, a także facetem, który stworzył ten
gets(stdin)
hack.Komentarze do tego posta są poprawne, że jest to forma znajdź i zamień, więc pozwól mi wyjaśnić, dlaczego zrobiłem to naprawdę szybko.
Kiedy stworzyłem tę witrynę (około 2012), obsługiwała ona tylko JavaScript. Nie było sposobu, aby „wczytać dane wejściowe” w JavaScript działającym w przeglądarce, więc pojawiłaby się funkcja
foo(input)
i użyłemreadline()
funkcji z Node.js, aby ją nazwaćfoo(readline())
. Z wyjątkiem tego, że byłem dzieckiem i nie wiedziałem lepiej, więc dosłownie zastąpiłemreadline()
dane wejściowe w czasie wykonywania. Tak sięfoo(readline())
stałofoo(2)
lubfoo("hello")
co działało dobrze dla JavaScript.Około 2013/2014 dodałem więcej języków i korzystałem z usług innych firm do oceny kodu online, ale bardzo trudno było wykonać stdin / stdout z usługami, z których korzystałem, więc utknąłem z tym samym głupim znajdź-i-zamień dla języków jak Python, Ruby i ostatecznie C ++, C # itp.
Szybko do przodu, uruchamiam kod w moich własnych kontenerach, ale nigdy nie zaktualizowałem sposobu, w jaki działa stdin / stdout, ponieważ ludzie przyzwyczaili się do dziwnego włamania (niektórzy nawet pisali na forach, wyjaśniając, jak to obejść).
Wiem, że to nie jest najlepsza praktyka i nie jest pomocne, aby ktoś uczący się nowego języka widział takie hacki, ale pomysł był taki, aby nowi programiści nie martwili się w ogóle czytaniem danych wejściowych i po prostu skupili się na pisaniu algorytmu, aby rozwiązać problem problem. Jedną z częstych skarg dotyczących witryn z wyzwaniami dla kodowania lata temu było to, że nowi programiści spędzali dużo czasu tylko na zastanawianiu się, jak czytać
stdin
lub czytać wiersze z pliku, więc chciałem, aby nowi programiści uniknęli tego problemu w Coderbyte.Niedługo zaktualizuję całą stronę edytora wraz z domyślnym kodem i
stdin
czytaniem języków. Miejmy nadzieję, że programiści C ++ będą bardziej zadowoleni z używania Coderbyte :)źródło
TAKE_INPUT
, a następnie użyć polecenia znajdź-zamian, aby wstawić#define TAKE_INPUT whatever_here
na górze.Jestem zaintrygowany. Czas więc założyć gogle śledcze, a ponieważ nie mam dostępu do kompilatora ani flag kompilacji, muszę się wykazać pomysłowością. Ponieważ nic w tym kodzie nie ma sensu, nie jest to zły pomysł, kwestionuj każde założenie.
Najpierw sprawdźmy rzeczywisty typ
gets
. Mam na to małą sztuczkę:A to wygląda ... normalnie:
gets
jest oznaczony jako przestarzały i ma podpischar *(char *)
. Ale jak wyglądaFirstFactorial(gets(stdin));
kompilacja?Spróbujmy czegoś innego:
Co daje nam:
Wreszcie jesteśmy coraz coś:
decltype(8)
. Więc całośćgets(stdin)
została zastąpiona tekstowo przez input (8
).I rzeczy stają się dziwniejsze. Błąd kompilatora jest kontynuowany:
Więc teraz otrzymujemy oczekiwany błąd dla
cout << FirstFactorial(gets(stdin));
Sprawdziłem, czy nie ma makra, a ponieważ
#undef gets
wydaje się nic nie robić, wygląda na to, że nie jest to makro.Ale
Kompiluje się.
Ale
Nie z oczekiwanym błędem na
n2
linii.I znowu, prawie każda modyfikacja
main
powoduje, że liniacout << FirstFactorial(gets(stdin));
wypluwa oczekiwany błąd.Co więcej,
stdin
faktycznie wydaje się być pusty.Mogę więc tylko wywnioskować i spekulować, że mają mały program, który analizuje źródło i próbuje (słabo) zastąpić
gets(stdin)
go wartością wejściową przypadku testowego, zanim faktycznie wprowadzi go do kompilatora. Jeśli ktoś ma lepszą teorię lub naprawdę wie, co robi, podziel się nim!To oczywiście bardzo zła praktyka. Podczas badania tego stwierdziłem, że jest tu przynajmniej pytanie ( przykład ) na ten temat, a ponieważ ludzie nie mają pojęcia, że istnieje strona, która to robi, ich odpowiedź brzmi "nie
gets
używaj ... zamiast tego", co rzeczywiście jest dobra rada, ale tylko bardziej dezorientuje OP, ponieważ każda próba prawidłowego odczytu ze standardowego wejścia zakończy się niepowodzeniem w tej witrynie.TLDR
gets(stdin)
jest nieprawidłowy C ++. To sztuczka, której używa ta konkretna witryna (z jakich powodów nie mogę się dowiedzieć). Jeśli chcesz nadal publikować na stronie (ani nie popieram ani nie popieram), musisz użyć tej konstrukcji, która w przeciwnym razie nie miałaby sensu, ale pamiętaj, że jest krucha. Prawie każda modyfikacjamain
spowoduje wyświetlenie błędu. Poza tą witryną używaj normalnych metod odczytu danych wejściowych.źródło
std::cout << "gets(stdin)";
a wynik to8
(lub cokolwiek wpiszesz w polu „input”. To haniebne nadużycie języka."gets(stdin)"
. To dosłowny ciąg znaków, którego nawet preprocesor nie tknąłbyPróbowałem następującego dodatku do
main
w edytorze Coderbyte:Gdzie tajemniczy i enigmatyczny fragment
gets(stdin)
pojawia się wewnątrz literału ciągu. Nie powinno to być w żaden sposób przekształcane, nawet przez preprocesor, a każdy programista C ++ powinien oczekiwać, że ten kod wypisze dokładny ciąggets(stdin)
na standardowe wyjście. A jednak widzimy następujące dane wyjściowe, gdy są kompilowane i uruchamiane na coderbyte:Gdzie wartość
8
jest pobierana bezpośrednio z wygodnego pola „wejście” w edytorze.Z tego jasno wynika, że ten edytor online wykonuje ślepe operacje znajdowania i zamieniania na kodzie źródłowym, zastępując wygląd
gets(stdin)
przez „dane wejściowe” użytkownika. Osobiście nazwałbym to niewłaściwym użyciem języka, które jest gorsze niż nieostrożne makra preprocesora.W kontekście strony internetowej z wyzwaniami kodowania online martwię się tym, ponieważ uczy ona niekonwencjonalnych, niestandardowych, bezsensownych i przynajmniej niebezpiecznych praktyk, takich jak
gets(stdin)
iw sposób, którego nie można powtórzyć na innych platformach.Jestem pewien, że nie może być tak trudno po prostu używać
std::cin
i przesyłać strumieniowo danych wejściowych do programu.źródło
gets(stdin)
tej zamiany? Miałem na myśli „ślepy” w tym sensie, że wydaje się być nieświadomy składni lub gramatyki języka.System.out.print(FirstFactorial(s.nextLine()9));
drukowana,89
nawet jeślis
jest niezdefiniowana.