Что происходит с "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, что само по себе плохо.
Но потом я понимаю: gets
имеет тип char*(char*)
. Таким образом, он не должен принимать параметр FILE*
и результат не должен использоваться вместо параметра int
, но... он не только компилируется без каких-либо предупреждений или ошибок, но и работает и фактически передает правильное входное значение FirstFactorial
За пределами этого конкретного сайта код не компилируется (как и ожидалось), так что здесь происходит?
* На самом деле первый using namespace std
но это не имеет отношения к моей проблеме здесь.
Ответы
Ответ 1
Я основатель Coderbyte, а также парень, который создал это, gets(stdin)
хак.
Комментарии к этому сообщению верны, так как это форма поиска и замены, поэтому позвольте мне объяснить, почему я сделал это очень быстро.
Когда я впервые создал сайт (около 2012 года), он поддерживал только JavaScript. В JavaScript не было возможности "читать входные данные", работающие в браузере, и поэтому была бы функция foo(input)
и я использовал функцию readline()
из Node.js, чтобы вызывать ее как foo(readline())
, За исключением того, что я был ребенком и не знал лучше, поэтому я буквально заменил readline()
на ввод во время выполнения. Таким образом, foo(readline())
стал foo(2)
или foo("hello")
который отлично работал для JavaScript.
Примерно в 2013/2014 году я добавил больше языков и использовал сторонние сервисы для оценки кода в сети, но было очень сложно использовать stdin/stdout с сервисами, которые я использовал, поэтому я остался с тем же глупым поиском и заменой языков например, Python, Ruby и, в конце концов, C++, С# и т.д.
Забегая вперед, я запускаю код в своих собственных контейнерах, но никогда не обновляю способ работы stdin/stdout, потому что люди привыкли к странному хаку (некоторые люди даже публиковали сообщения на форумах, объясняющие, как его обойти).
Я знаю, что это не лучшая практика, и для кого-то, изучающего новый язык, бесполезно видеть подобные хаки, но идея в том, чтобы новые программисты вообще не беспокоились о чтении ввода и просто сосредоточились на написании алгоритма для решения проблемы. проблема. Еще одна распространенная жалоба на проблемы с кодированием сайтов много лет назад заключалась в том, что новые программисты тратят много времени на то, чтобы понять, как читать из stdin
или читать строки из файла, поэтому я хотел, чтобы новые кодеры избежали этой проблемы на Coderbyte.
Скоро я обновлю всю страницу редактора вместе с кодом по умолчанию и чтением стандартного stdin
для языков. Надеемся, что тогда программисты C++ получат больше удовольствия от использования Coderbyte :)
Ответ 2
Я заинтригован. Итак, пришло время надеть защитные очки, и, поскольку у меня нет доступа к компилятору или флагам компиляции, мне нужно проявить изобретательность. Кроме того, потому что ничто в этом коде не имеет смысла, это не плохая идея, ставящая под сомнение каждое предположение.
Сначала позвольте проверить фактический тип 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)
был заменен на текстовый ввод input (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, так как любая попытка корректного чтения из stdin потерпит неудачу на этом сайте.
TL;DR
get gets(stdin)
недействителен C++. Это трюк, который использует этот конкретный сайт (по каким причинам я не могу понять). Если вы хотите продолжить отправку на сайт (я не одобряю и не одобряю его), вы должны использовать эту конструкцию, которая в противном случае не имела бы смысла, но помните, что она хрупкая. Практически любые изменения в main
приведут к ошибке. За пределами этого сайта используйте обычные методы чтения ввода.
Ответ 3
Я попробовал следующее дополнение к main
в редакторе Coderbyte:
std::cout << "gets(stdin)";
Где таинственный и загадочный фрагмент gets(stdin)
появляется внутри строкового литерала. Это не должно быть преобразовано ничем, даже препроцессором, и любой программист C++ должен ожидать, что этот код напечатает точную строку, полученную gets(stdin)
в стандартный вывод. И все же мы видим следующий вывод при компиляции и запуске на кодербайте:
8
Где значение 8
берется прямо из удобного поля ввода под редактором.
Из этого становится ясно, что этот онлайн-редактор выполняет слепые операции поиска и замены над исходным кодом, заменив внешний вид gets(stdin)
пользовательским "вводом". Я бы лично назвал это злоупотреблением языком, которое хуже небрежного макроса препроцессора.
В контексте веб-сайта, посвященного проблемам онлайн-кодирования, меня это беспокоит, потому что он учит нетрадиционным, нестандартным, бессмысленным и, по крайней мере, небезопасным практикам, таким как gets(stdin)
, и таким образом, который не может повторяться на других платформ.
Я уверен, что это не может быть так сложно просто использовать std::cin
и просто поток ввода в программу.