Преимущество scanf
состоит в том, что, как только вы научитесь использовать инструмент, как вы всегда должны делать в C, он имеет чрезвычайно полезные варианты использования. Вы можете узнать, как использовать scanf
и друзей, чтение и понимание руководство . Если вы не можете прочитать это руководство без серьезных проблем с пониманием, это, вероятно, будет означать, что вы не очень хорошо знаете C.
scanf
и друзья пострадали от неудачного выбора дизайна , что затруднило (а иногда и невозможно) правильно использовать без чтения документации, как показали другие ответы. К сожалению, это происходит во всем C, поэтому если бы я не советовал использовать scanf
, то я бы, вероятно, не советовал использовать C.
Одним из самых больших недостатков, по-видимому, является чисто репутация, которую он заработал среди непосвященных ; как и со многими полезными функциями C, мы должны быть хорошо информированы, прежде чем использовать его. Ключевым моментом является осознание того, что, как и в случае с остальной частью C, оно кажется лаконичным и идиоматичным, но это может быть слегка обманчивым. Это распространено в C; новичкам легко написать код, который, по их мнению, имеет смысл и может даже работать на них изначально, но не имеет смысла и может привести к катастрофическим ошибкам.
Например, непосвященные обычно ожидают, что делегат %s
вызовет строку для чтения, и хотя это может показаться интуитивно понятным, это не обязательно так. Более уместно описать поле, читаемое как слово . Чтение руководства настоятельно рекомендуется для каждой функции.
Каким будет любой ответ на этот вопрос, если не упомянуть отсутствие безопасности и риск переполнения буфера? Как мы уже говорили, C не является безопасным языком и позволит нам срезать углы, возможно, применить оптимизацию за счет корректности или, скорее, потому что мы ленивые программисты. Таким образом, когда мы знаем, что система никогда не получит строку, размер которой превышает фиксированное число байтов, мы получаем возможность объявить массив такого размера и отказаться от проверки границ. Я действительно не вижу в этом падения; это вариант. Снова, чтение руководства настоятельно рекомендуется, и это откроет нам эту возможность.
Ленивые программисты не единственные, кого ужалила scanf
. Нередко люди видят, например, значения float
или double
, используя %d
. Они обычно ошибаются, полагая, что реализация будет выполнять какое-то преобразование за кулисами, что имело бы смысл, потому что подобные преобразования происходят во всем остальном языке, но здесь это не так. Как я уже говорил ранее, scanf
и друзья (и, конечно, остальная часть C) обманчивы; они кажутся краткими и идиоматичными, но это не так.
Неопытные программисты не обязаны учитывать успех операции . Предположим, что пользователь вводит что-то совершенно нечисловое, когда мы сказали scanf
прочитать и преобразовать последовательность десятичных цифр, используя %d
. Единственный способ, которым мы можем перехватить такие ошибочные данные, - это проверить возвращаемое значение, и как часто мы пытаемся проверить возвращаемое значение?
Так же, как fgets
, когда scanf
и друзья не могут прочитать то, что им говорят, поток останется в необычном состоянии;
- В случае fgets
, если недостаточно места для хранения полной строки, тогда оставшаяся непрочитанная строка может быть ошибочно обработана, как если бы это была новая строка, если ее нет.
- В случае scanf
и друзей преобразование не удалось, как описано выше, ошибочные данные остаются непрочитанными в потоке и могут быть ошибочно обработаны, как если бы они являлись частью другого поля.
Не проще использовать scanf
и друзей, чем использовать fgets
. Если мы проверим успех, ищем '\n'
, когда мы используем fgets
или проверяем возвращаемое значение, когда мы используем scanf
и друзей, и мы обнаруживаем, что прочитали неполную строку, используя fgets
или если не удалось прочитать поле с использованием scanf
, то мы столкнулись с той же реальностью: мы, скорее всего, откажемся от ввода (обычно вплоть до следующего символа новой строки)! Yuuuuuuck!
К сожалению, scanf
одновременно делает сложным (неинтуитивным) и легким (наименьшее количество нажатий клавиш) отбрасывание ввода таким способом. Столкнувшись с этой реальностью отказа от пользовательского ввода, некоторые пытались scanf("%*[^\n]%*c");
, не понимая, что делегат %*[^\n]
потерпит неудачу, когда встретится только с новой строкой, и, следовательно, новая строка все равно останется на поток.
Небольшая адаптация, разделив два делегата формата, и мы видим здесь некоторый успех: scanf("%*[^\n]"); getchar();
. Попробуйте сделать это с таким небольшим количеством нажатий клавиш, используя другой инструмент;)