Что происходит с "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 берется прямо из удобного поля ввода под редактором.

Magic code

Из этого становится ясно, что этот онлайн-редактор выполняет слепые операции поиска и замены над исходным кодом, заменив внешний вид gets(stdin) пользовательским "вводом". Я бы лично назвал это злоупотреблением языком, которое хуже небрежного макроса препроцессора.

В контексте веб-сайта, посвященного проблемам онлайн-кодирования, меня это беспокоит, потому что он учит нетрадиционным, нестандартным, бессмысленным и, по крайней мере, небезопасным практикам, таким как gets(stdin), и таким образом, который не может повторяться на других платформ.

Я уверен, что это не может быть так сложно просто использовать std::cin и просто поток ввода в программу.