Coderbyte - это сайт, посвященный онлайн-программированию (я нашел его всего 2 минуты назад).

Первая задача C ++, с которой вас встречают, имеет скелет C ++, который необходимо изменить:

# включить 
#include <строка>
используя пространство имен std;

int FirstFactorial (int num) {

  // Код идет сюда
  return num;

}

int main () {

  // Сохраняем этот вызов функции здесь
  cout << FirstFactorial (получает (stdin));
  возврат 0;

}

Если вы мало знакомы с C ++, первое, что* бросается в глаза, это:

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

Итак, код вызывает , получает, который устарел с C ++ 11 и удален с C ++ 14, что само по себе плохо.

Но потом я понимаю: получает имеет тип char * (char *). Таким образом, он не должен принимать параметр FILE *, и результат не должен использоваться вместо параметра int, но ... не только он компилируется без каких-либо предупреждений или ошибок, но он запускается и фактически передает правильное входное значение в FirstFactorial.

За пределами этого конкретного сайта код не компилируется (как ожидалось), так что здесь происходит?


* На самом деле первый - с использованием пространства имен std, но это не имеет отношения к моей проблеме здесь.

bolov

Ответы (3)

Я заинтригован. Итак, пора надеть очки расследования, и, поскольку у меня нет доступа к компилятору или флагам компиляции, мне нужно проявить изобретательность. Кроме того, поскольку ничто в этом коде не имеет смысла, неплохо ставить под сомнение каждое предположение.

Сначала проверим фактический тип получает. У меня для этого есть небольшая хитрость:

template  struct Name;

int main() { 
    
    Name n;
  
  // keep this function call here
  cout << FirstFactorial(gets(stdin));
  return 0;
    
}

А это выглядит ... нормально:

/ tmp / 613814454 / Main.cpp: 16: 19: предупреждение: 'gets' устарело [-Wdeprecated-declrations]
    Имя  n;
                  ^
/usr/include/stdio.h:638:37: примечание: здесь 'gets' явно помечено как устаревшее
extern char * gets (char * __ s) __wur __attribute_deprecated__;
                                    ^
/usr/include/x86_64-linux-gnu/sys/cdefs.h:254:51: примечание: расширено из макроса '__attribute_deprecated__'
# определить __attribute_deprecated__ __attribute__ ((__deprecated__))
                                                  ^
/tmp/613814454/Main.cpp:16:26: ошибка: неявное создание экземпляра неопределенного шаблона 'Name '
    Имя  n;
                         ^
/tmp/613814454/Main.cpp:12:25: примечание: здесь объявлен шаблон
шаблон <класс> Имя структуры;
                        ^
Создано 1 предупреждение и 1 ошибка.

получает помечается как устаревшее и имеет подпись char * (char *). Но как же тогда FirstFactorial (gets (stdin)); компиляция?

Давайте попробуем еще что-нибудь:

int main() { 
  Name n;
  
  // keep this function call here
  cout << FirstFactorial(gets(stdin));
  return 0;
    
} 

Что дает нам:

/ tmp / 286775780 / Main.cpp: 15: 21: ошибка: неявное создание экземпляра неопределенного шаблона 'Name '
  Имя  n;
                    ^

В итоге получаем что-то: decltype (8). Таким образом, все gets (stdin) было текстуально заменено вводом (8).

И все становится еще страннее. Ошибка компилятора продолжается:

/ tmp / 596773533 / Main.cpp: 18: 26: ошибка: нет соответствующей функции для вызова «получает»
  cout << FirstFactorial (получает (stdin));
                         ^ ~~~
/usr/include/stdio.h:638:14: примечание: функция-кандидат не жизнеспособна: нет известного преобразования из 'struct _IO_FILE *' в 'char *' для 1-го аргумента
extern char * gets (char * __ s) __wur __attribute_deprecated__;

Итак, теперь мы получаем ожидаемую ошибку для cout << FirstFactorial (gets (stdin));

Я проверил макрос, и, поскольку # undef получает, похоже, ничего не делает, похоже, что это не макрос.

А

std::integral_constant n;

Компилируется.

А

std::integral_constant n;    // OK
std::integral_constant n2;   // ERROR                                          wtf??

Не вызывает ожидаемую ошибку в строке n2.

И снова, почти любая модификация main делает строку cout << FirstFactorial (gets (stdin)); выдаёт ожидаемую ошибку.

Более того, stdin на самом деле кажется пустым.

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

Очевидно, это очень плохая практика. Изучая это, я обнаружил, что здесь есть как минимум вопрос (пример) по этому поводу, и поскольку люди понятия не имеют, что существует сайт, который делает это, их ответ: «Не используйте . получает использовать ... вместо », что действительно является хорошим советом, но только больше сбивает с толку OP, поскольку любая попытка корректного чтения из стандартного ввода не удастся на этом сайте.


TL; DR

получает (stdin) недействителен C ++. Это уловка, которую использует этот конкретный сайт (по каким причинам я не могу понять). Если вы хотите продолжить отправку на сайт (я ни одобряю, ни одобряю), вы должны использовать эту конструкцию, которая в противном случае не имела бы смысла, но помните, что она хрупкая. Практически любые модификации main выдадут ошибку. За пределами этого сайта используйте обычные методы чтения ввода.

Я основатель Coderbyte, а также парень, который создал это получает (stdin) hack.

Комментарии к этому посту верны, так как это форма поиска и замены, поэтому позвольте мне объяснить, почему я сделал это так быстро.

В те дни, когда я впервые создал сайт (примерно в 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 ++, C # и т. д.

• 100001 .

Я знаю, что это не лучшая практика, и для тех, кто изучает новый язык, бесполезно видеть подобные хаки, но идея заключалась в том, чтобы новые программисты вообще не беспокоились о чтении ввода и просто сосредоточились на написании алгоритма решить проблему. Одна из распространенных жалоб на сайты с проблемами кодирования много лет назад заключалась в том, что новые программисты тратили много времени, просто выясняя, как читать из stdin или читать строки из файла, поэтому я хотел, чтобы новые программисты избегали этой проблемы на Кодербайт.

Я скоро обновлю всю страницу редактора вместе с кодом по умолчанию и stdin чтением для языков. Надеюсь, тогда программисты на C ++ будут больше любить Coderbyte :)

Попробовал следующее дополнение к main в редакторе Coderbyte:

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

Где таинственный и загадочный фрагмент получает (stdin) появляется внутри строкового литерала. Это не должно быть преобразовано ничем, даже препроцессором, и любой программист на C ++ должен ожидать, что этот код напечатает точную строку gets (stdin) на стандартный вывод. И все же мы видим следующий результат при компиляции и запуске в кодербайте:

8

Где значение 8 берется прямо из удобного поля ввода под редактором.

Magic code

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

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

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

2022 WebDevInsider