Давайте посмотрим, как эти функции работают, написав программу, которая отображает введенную строку, но заменяет каждый отличный от пробела символ следующим за ним символом в последовательности кодов ASCII. Пробелы будут выводиться без изменений. Результат можно сформулировать так: “Если символ является пробелом, он выводится, в противном случае выводится символ, следующий за ним в последовательности кодов ASCII”.
Код программы представлен в листинге 7.2.
Листинг 7.2. Программа cypher1.с

(Пусть вас не беспокоит предупреждение компилятора о возможной потере данных. Все прояснится в главе 8 при рассмотрении последовательности EOF.)
Ниже показан результат выполнения программы:
CALL ME HAL.
DBMM NF IBM/
Сравните этот цикл с циклом из листинга 7.1. В листинге 7.1 для определения момента прекращения цикла применялось возвращаемое значение scanf(), а не значение введенного элемента. Однако в листинге 7.2 для этой цели используется значение самого введенного элемента. Такое отличие в результате приводит к несколько другой структуре цикла, с одним оператором чтения перед циклом и еще одним оператором
Управляющие операторы С: ветвление и переходы 253
чтения в конце цикла. Тем не менее, гибкий синтаксис языка С позволяет эмулировать программу из листинга 7.1 за счет объединения чтения и проверки в одно выражение. То есть цикл следующего вида:
ch = getchar(); /* читать символ */
while (ch != '\n') /* пока не встретится конец строки */
{
... /* обработать символ */
ch = getchar(); /* получить следующий символ */
}
можно заменить таким циклом:
while ((ch = getchar()) != '\n')
{
/* обработать символ */
}
Интерес представляет следующая строка:
while ((ch = getchar()) != '\n')
Она демонстрирует характерный стиль программирования на языке С — объединение двух действий в одно выражение. Возможность свободного форматирования в С помогает сделать отдельные компоненты строки яснее:
while (
(ch = getchar()) // присвоить значение переменной ch
!= '\n') // сравнить ch с \n
Действиями являются присваивание значения переменной ch и ее сравнение с символом новой строки. Круглые скобки, в которые заключено выражение ch = getchar(), делают его левым операндом операции ! =. Чтобы вычислить это выражение, сначала вызывается функция getchar(), после чего возвращаемое ею значение присваивается переменной ch. Поскольку значением выражения присваивания является значение его левого члена, значением ch = getchar() будет как раз новое значение ch. Таким образом, после того как значение для ch прочитано, условие проверки сводится к ch != ‘\n' (т.е. значение ch неравно символу новой строки).
Конструкция подобного рода весьма распространена в программировании на С, поэтому вы должны быть с ней знакомы. Кроме того, не забывайте с помощью круглых скобок правильно группировать подвыражения.
Все скобки обязательны. Предположим, что вы по ошибке записали следующее выражение:
while (ch = getchar() != '\n')
Операция != имеет более высокий приоритет, чем =, следовательно, первым будет вычислено выражение getchar() ! = ‘\n'. Поскольку это условное выражение, оно принимает значение 1 или 0 (истина или ложь). Затем это значение присваивается переменной ch. Отсутствие скобок означает, что ch будет присвоено 0 или 1, а не возвращаемое значение функции getchar(); это совсем не то, что планировалось.
Оператор
putchar(ch + 1); /* изменить другие символы */
еще раз иллюстрирует, что символы хранятся в виде целых чисел. В выражении ch + 1 тип переменной ch расширяется до int для выполнения вычислений, а результирующее значение int nередается в функцию putchar(), которая принимает аргумент типа int, но при отображении символа задействует только последний байт этого значения.
254 глава 7
Семейство функций для работы с символами ctype.h
Обратите внимание, что в выводе программы из листинга 7.2 точка была преобразована в косую черту; причина в том, что в ASCII код символа косой черты на единицу больше, чем код символа точки. Однако если цель программы заключается в преобразовании только букв, было бы неплохо оставлять неизменными все небуквениые символы, а не только пробелы. Логические операции, обсуждаемые позже в главе, предоставляют способ проверки, не является ли символ пробелом, запятой и т.д., но перечислять все возможные варианты было бы довольно утомительно. К счастью, в С имеется стандартный набор функций для анализа символов; их прототипы содержатся в заголовочном файле ctype.h Эти функции принимают символ в качестве аргумента и возвращают ненулевое значение (истина), если символ принадлежит к конкретной категории, и ноль (ложь) в противном случае. Например, функция isalpha() возвращает ненулевое значение, если ее аргумент является буквой. В листинге 7.3 обобщается программа из листинга 7.2 за счет применения этой функции; здесь также задействована только что рассмотренная укороченная структура цикла.
Читать дальше