2016-09-25 2 views
1

Смотрите следующий код:Почему scanf принимает больше символов, чем в буфере есть место?

int main() 
{ 
    char test[3]; 

    scanf("%s", test); 
    __fpurge(stdin); 
    printf("%s", test); 
} 

Программа должна записать только 3 символов, но когда я типа, например, 8 символов, программа записывает все 8! Этого не должно быть. Правильно будет записывать 3 символа, потому что это scanf?

+0

Это * неопределенное поведение *. Это означает, что все может произойти, включая, по-видимому, правильную работу. Я не привязывал шнурки к обуви. Почему я не упал? Это вопрос. –

+1

Вы не сказали 'scanf()', сколько места было доступно для данных, поэтому предполагается, что для всего, что было введено, должно быть достаточно свободного места. Поэтому он делает все возможное, чтобы сохранить 8 символов, но это переполняет указанный вами массив, что приводит к неопределенному поведению. Если вы учитесь на современном языке с автоматической защитой от переполнения буфера, обучение C будет грубым. Среда C не защищает вас от вашего недоразумения или небрежного поведения. (И 'char test [3]' может содержать только двухсимвольную строку плюс нулевой ограничитель.) –

+0

Любопытно, почему вы _expect_ C не принимали больше символов, чем в буфере есть место? – chux

ответ

2

scanf принимает больше данных, чем вы можете поместиться в test, потому что вы разрешаете это делать, используя %s без ограничения. Это опасно, и его следует избегать в производственном коде.

Замените %s на %3s, чтобы устранить эту проблему. Если вы хотите прочитать три символа, test должны быть четыре символа шириной для размещения нулевого терминатора:

char test[4]; 
scanf("%3s", test); 
3

Когда вы передаете test к scanf(), вы не передаете ничего, кроме указателя на первый символ вашего буфера, так scanf() не знает, насколько велик ваш буфер. Он с радостью примет столько символов, сколько вы наберете, и он сохранит их там. Таким образом, когда вы вводите более двух символов, вы вызываете scanf() для записи символов (плюс нулевой символ символа asciiz) за конец вашего буфера. Как правило, в этом случае следует ожидать краха программы.

Тот факт, что вы не испытали сбой, в основном совпадение, вероятно, происходит потому, что компилятор выделил место для более чем трех символов в стеке из-за соображений выравнивания, возможно, для 8 символов и более. Если вы наберете достаточное количество символов, ваша программа наверняка сбой.

По этой причине это использование scanf() считается полностью опасным. Никогда не следует использовать scanf(), когда делаете серьезное кодирование. Вместо этого вы должны указать ширину своей строки, например: "%2s". (Обратите внимание, что вы должны указать число, которое меньше размера вашего буфера на единицу, для учета нулевого символа терминатора asciiz, который будет автоматически добавлен scanf().)

+1

Вы должны проиллюстрировать необходимое исправление на '% s' (т. Е.'% 2s') ... –

+0

@JonathanLeffler да, я просто сделал, я смотрел документацию. Я вспомнил синтаксис строки формата, где вы могли передать буфер и размер буфера в качестве параметров, вместо того, чтобы вставлять размер буфера в строковый литерал, но я не смог его найти. Вероятно, это расширение Microsoft C. –

+2

@MikeNakis, который будет 'scanf_s', который нуждается в дополнительных аргументах для размера некоторых типов спецификатора формата. Предполагается, что это «безопаснее», но на самом деле это так же сложно, как и исходный «scanf», и если вы ошибаетесь, на самом деле *** более *** опасно, поскольку вы можете ошибочно проходить размер аргумент, такой как '2' для аргумента * address *. –

Смежные вопросы