Основная причина заключается в том, что по умолчанию интерактивный стандартный ввод буферизуется по строкам (и его изменение нетривиально). Таким образом, когда пользователь начинает вводить свой ввод, он не сразу предоставляется вашей программе; только когда пользователь нажимает Enter .
Мы определили макрос MAX_LINE_LEN
ранее, потому что fgets()
может читать только строку, если указан буфер, и будет возвращать остаток этой строки в следующих вызовах.
(Вы можете проверить, закончилась ли прочитанная строка новой строкой: если это не так, то либо это была последняя строка во входном файле, которая не имеет новой строки в конце последней строки, либо строка была дольше, чем у вас есть буфер, поэтому вы получили только начальную часть, а остальная часть строки все еще ждет вас в буфере.)
+1
в char buffer[MAX_LINE_LEN + 1];
- потому что строки в C заканчиваются нулевым символом, '\0'
, в конце. Таким образом, если у нас есть буфер из 19 символов, он может содержать строку максимум из 18 символов.
Обратите внимание, что NUL или nul с одним ell является именем символа ASCII с кодом 0, '\0'
и является символом маркера конца строки.
NULL (или иногда ноль), однако, является указателем на нулевой адрес, а в C99 и более поздних версиях совпадает с (void *)0
. Это сторожевое значение и значение ошибки, которое мы используем, когда мы хотим установить указатель на распознаваемое значение ошибки / неиспользуемого / пустого значения вместо указания на фактические данные.
sizeof buffer
- это общее количество символов (включая nul char в конце строки), используемое переменной buffer
.
В этом случае мы могли бы использовать MAX_LINE_LEN + 1
вместо этого (второй параметр fgets()
- это количество символов в данном буфере, , включая резервирование для конца строковый символ).
Причина, по которой я использовал sizeof buffer
, заключается в том, что это так полезно. (Помните, что если бы buffer
был указателем, а не массивом, он оценивал бы размер указателя; не количество данных, доступных для того, на что указывает указатель. Если вы используете указатели, вам нужно будет отслеживать количество памяти, доступной там самостоятельно, обычно в отдельной переменной. Именно так работает Си.)
А также потому, что важно, чтобы sizeof
был не функцией, а оператором: он не оценивает свой аргумент, он учитывает только размер (типа) аргумента. Это означает, что если вы сделаете что-то глупое, например sizeof (i++)
, вы обнаружите, что i
не увеличено, и что оно дает то же значение, что и sizeof i
. Опять же, это потому, что sizeof
является оператором, а не функцией, и он просто возвращает размер своего аргумента.
fgets()
возвращает указатель на строку, сохраненную в буфере, или NULL
, если произошла ошибка.
Именно поэтому я назвал указатель line
и массив хранения buffer
. Они описывают мои намерения как программиста. (Кстати, это очень важно при написании комментариев: не описывайте, что делает код, потому что мы можем прочитать код; но опишите ваше намерение относительно того, что код должен делать, потому что только программист знает это, но это важно знать это намерение, если кто-то пытается понять, изменить или исправить код.)
Семейство функций scanf () возвращает количество успешных преобразований. Чтобы обнаружить ввод, в котором правильное числовое значение сопровождается мусором, скажем 1.0 x
, я попросил sscanf()
игнорировать любые пробелы после числа (пробел означает символы табуляции, пробелы и символы новой строки; '\t'
, '\n'
, '\v'
, '\f'
, '\r'
и ' '
для языка C по умолчанию, использующего набор символов ASCII), и попробуйте преобразовать один дополнительный символ, dummy
.
Теперь, если в строке есть что-то, кроме пробела после числа, sscanf()
сохранит первый символ этого чего-либо в dummy
и вернет 2. Однако, потому что я хочу только строки, которые содержат только число и нет фиктивных символов, я ожидаю возвращаемое значение 1.
Чтобы обнаружить q
или Q
(но только как первый символ в строке), мы просто исследуем первый символ в line
, line[0]
.
Если бы мы включили <string.h>
, мы могли бы использовать, например, if (strchr(line, 'q') || strchr(line, 'Q'))
чтобы увидеть, есть ли где-нибудь в поставляемой строке q
или Q
. strchr(string, char)
возвращает указатель на первое вхождение char в строку или NULL, если его нет; и все указатели, кроме NULL, считаются логически верными. (То есть мы могли бы эквивалентно написать if (strchr(line, 'q') != NULL || strchr(line, 'Q') != NULL)
.)
Другая функция, которую мы могли бы использовать, объявленная в <string.h>
, это strstr()
. Он работает как strchr()
, но вторым параметром является строка. Например, (strstr(line, "exit"))
имеет значение true, только если line
где-то содержит exit
. (Это может быть brexit
или exitology
, хотя; это просто поиск по подстроке.)
В цикле continue
пропускает остаток тела цикла и начинает следующую итерацию тела цикла с начала.
В цикле break
пропускает остаток тела цикла и продолжает выполнение после цикла.
EXIT_SUCCESS
и EXIT_FAILURE
- стандартные коды состояния выхода, определяемые <stdlib.h>
. Большинство предпочитают использовать 0
для EXIT_SUCCESS (потому что именно так оно и есть в большинстве операционных систем), но я думаю, что такое описание успеха / неудачи облегчает чтение кода.