Главная страница
Навигация по странице:

  • 1.4. Именованные константы

  • 1.5. Ввод-вывод символов

  • 32 _ Глава Обзор языка

  • Упражнение Напишите программу для подсчета пробелов, табуляций и новых строк.Упражнение 1.9.

  • 36 __ _ Глава Обзор языка

  • Упражнение 1.11.

  • Б. Керриган, Д. Ритчи Язык программирования C. Б. Керниган, Д. зык программирования и . Издание 3е, исправленное Перевод с английского под редакцией Вс. С. Штаркмана СанктПетербург 2003


    Скачать 31.48 Mb.
    НазваниеБ. Керниган, Д. зык программирования и . Издание 3е, исправленное Перевод с английского под редакцией Вс. С. Штаркмана СанктПетербург 2003
    АнкорБ. Керриган, Д. Ритчи Язык программирования C.pdf
    Дата06.04.2017
    Размер31.48 Mb.
    Формат файлаpdf
    Имя файлаБ. Керриган, Д. Ритчи Язык программирования C.pdf
    ТипКнига
    #4546
    страница3 из 28
    1   2   3   4   5   6   7   8   9   ...   28
    Упражнение 1.5. Измените программу преобразования температур так,
    чтобы она печатала таблицу в обратном порядке, т. е. от 300 до 0.
    1.4. Именованные константы
    Прежде чем мы закончим рассмотрение программы преобразования температур, выскажем еще одно соображение. Очень плохо, когда по про-

    Ввод-вывод символов _ _ 29
    грамме рассеяны "загадочные числа", такие как 300, 20. Тот, кто будет читать программу, не найдет в них и намека на то, что они собой пред- ставляют. Кроме того, их трудно заменить на другие каким-то системати- ческим способом. Одна из возможностей справиться с такими числами - дать им имена. Строка ine определяет
    имя,
    или именованную константу, для заданной строки символов:
    имя
    С этого момента при любом появлении имени (если только оно встреча- ется не в тексте, заключенном в кавычки, и не является частью определе- ния другого имени) оно будет заменяться на соответствующий ему под-
    ставляемый-текст. Имя имеет тот же вид, что и переменная: последова- тельность букв и цифр, начинающаяся с буквы. Подставляемый-текст
    может быть любой последовательностью символов, среди которых могут встречаться не только цифры.
    LOWER 0 /* нижняя граница таблицы */
    UPPER 300 /* верхняя граница */
    STEP 20 /* размер шага */
    /* печать таблицы температур по Фаренгейту и Цельсию */
    {
    int fahr;
    i for
    = LOWER;
    <= UPPER; fahr =
    + STEP)
    Величины LOWER, UPPER и STEP - именованные константы, а не перемен- ные, поэтому для них нет объявлений. По общепринятому соглашению имена именованных констг.нт набираются заглавными буквами, чтобы они отличались от обычных переменных, набираемых строчными. Заметим,
    что в конце «def точка с запятой не ставится.
    1.5. Ввод-вывод символов
    Теперь мы намерены рассмотреть семейство программ по обработке текстов. Вы обнаружите, что многие существующие программы являют- ся просто расширенными версиями обсуждаемых здесь прототипов.

    30
    Обзор языка
    Стандартная библиотека поддерживает очень простую ввода- вывода. Текстовый ввод-вывод вне зависимости от того, откуда он исхо- дит или куда направляется, имеет дело с потоком символов. Текстовый
    поток - это последовательность символов, разбитая на строки, каждая из которых содержит нуль или более символов и завершается символом новой строки. Обязанность следить за тем, чтобы любой поток ввода-вы- вода отвечал этой модели, возложена на библиотеку: программист, пользу- ясь не должен заботиться о том, в каком виде строки пред- ставляются вне программы.
    Стандартная библиотека включает несколько функций для чтения и за- писи одного символа. Простейшие из них - г и
    г.
    одно обра- щение к getcha г считывается следующий символ ввода из текстового потока,
    и этот символ выдается в качестве результата. Так, после выполнения с =
    переменная с содержит очередной символ ввода. Обычно символы посту- пают с клавиатуры. Ввод из файлов рассматривается в главе 7.
    Обращение к putcha г приводит к печати одного символа. Так,
    putchar(c)
    напечатает содержимое целой переменной с в виде символа (обычно на экране). Вызовы и printf могут произвольным образом пере- межаться. Вывод будет формироваться в том же порядке, что и обраще- ния к этим функциям.
    Копирование файла
    При наличии функций getcha г и ничего больше не зная о вво- де-выводе, можно написать удивительно много полезных программ. Про- стейший пример - это программа, копирующая по одному символу с вход- ного потока в выходной поток:
    чтение символа
    while (символ не является признаком конца файла)
    вывод
    что прочитанного символа
    чтение символа
    Оформляя ее в виде программы на Си, получим

    /* копирование ввода на вывод; 1-я версия */
    {
    int с;

    1.5. Ввод-вывод символов
    с =.
    while (с
    EOF) {
    с = getchar();
    Оператор отношения ! = означает "не равно".
    Каждый вводимый с клавиатуры или появляющийся на экра- не, как и любой другой символ внутри машины, кодируется комбинацией битов. Тип специально предназначен для символьных дан- ных, однако для этого также годится и любой целый тип. Мы типом и делаем это по одной важной причине, которая требует разъяс- нений.
    Существует проблема: как отличить конец от обычных читаемых данных. Решение заключается в том, чтобы функция по исчерпа- нии входного потока выдавала в качестве результата такое значение, ко- торое нельзя было бы спутать ни с одним реальным символом. Это значе- ние есть EOF (аббревиатура от end
    - конец файла). Мы должны объя- вить переменную с такого типа, чтобы его "хватило" для представления всех возможных результатов, выдаваемых функцией
    Нам не под- ходит тип так как с должна быть достаточно "емкой", чтобы помимо любого значения типа char быть в состоянии хранить и EOF. Вот почему мы используем int, а не char.
    EOF - целая константа, определенная в
    Какое значение имеет эта константа - неважно, лишь бы оно отличалось от любого из возмож- ных значений типа
    Использование именованной константы с уни- фицированным именем гарантирует, что программа не будет зависеть от конкретного числового значения, которое, возможно, в других Си-систе- мах будет иным.
    Программу копирования можно написать более сжато. В Си любое присваивание, например с = getchar()
    трактуется как выражение со значением, равным значению левой части после присваивания. Это значит, что присваивание может встречаться внутри более сложного выражения. Если присваивание переменной с рас- положить в проверке условия цикла while, то программу копирования можно будет записать в следующем виде:
    /* копирование ввода на вывод; 2-я версия */

    32 _ Глава
    Обзор языка
    {
    int с;
    while
    =
    != EOF)
    Цикл while, пересылая в с полученное от get значение, сразу же про- веряет: не является ли оно "концом файла". Если это не так, выполняется тело цикла while и печатается символ. По окончании ввода завершается работа цикла while, а тем самым и main.
    В данной версии ввод "централизован" - в программе имеется только одно обращение к getcha г. В результате она более компактна и легче вос- принимается при чтении. Вам часто придется сталкиваться с такой фор- мой записи, где присваивание делается вместе с проверкой. (Чрезмерное увлечение ею, однако, может запутать программу, поэтому мы постара- емся пользоваться указанной формой разумно.)
    Скобки внутри условия, вокруг присваивания, необходимы. Приори-
    тет ! = выше, чем приоритет =, из следует, что при отсутствии скобок проверка ! = будет выполняться до операции присваивания =. Таким об- разом, запись с =
    EOF
    эквивалентна записи с = (getchar()
    EOF)
    А это совсем не то, что нам нужно: переменной с будет присваиваться О
    или 1 в зависимости от того, встретит или не встретит getcha г признак конца файла. (Более подробно об этом см. в главе 2.)
    Упражнение 1.6. Убедитесь в том, что выражение
    != EOF
    получает значение 0 или
    Упражнение 1.7. Напишите программу, печатающую значение EOF.
    1.5.2. Подсчет символов
    Следующая программа занимается подсчетом символов; она имеет много сходных черт с программой копирования.
    h>
    /* подсчет вводимых символов; 1-я версия */

    1.5. Ввод-вывод символов _ 33
    long nc;
    nc
    = 0;
    while (getchar()
    EOF)
    nc);
    }
    Инструкция
    ++nc;
    представляет новый оператор ++, который
    увеличить на
    Вместо этого можно было бы написать пс =
    но ++пс намного короче,
    а часто и эффективнее. Существует аналогичный оператор означа- ющий
    на единицу. Операторы ++ и — могут быть как префикс- ными (++пс), так и постфиксными (пс++). Как будет показано в главе эти две формы в выражениях имеют разные значения, но и ++пс, и пс++ добавля- ют к пс единицу. В данном случае мы остановились на префиксной записи.
    Программа подсчета символов накапливает сумму в переменной типа long. Целые типа long имеют не менее 32 битов. Хотя на некоторых маши- нах типы int и long имеют одинаковый размер, существуют, однако, ма- шины, в которых int занимает 16 битов с максимально возможным зна- чением 32767, а это - сравнительно маленькое число, и счетчик типа int может переполниться. Спецификация в printf указывает, что сорт- ветствующий аргумент имеет тип long.
    Возможно охватить еще больший диапазон значений, если использо- вать тип double (т. е. float с двойной точностью). Применим также ин- струкцию f o r вместо чтобы продемонстрировать другой способ написания цикла.
    /* подсчет вводимых символов; 2-я версия */
    {
    double nc;
    for (nc 0; getchar() != EOF; ++nc)
    nc);
    }
    В p r i n t f спецификатор применяется как для так и для double;
    спецификатор %. Of означает печать без десятичной точки и дробной час- ти (последняя в нашем случае отсутствует).
    1 1116

    34
    Глава
    Обзор языка
    Тело указанного f о пусто, поскольку кроме проверок и прира- щений счетчика делать ничего не нужно. Но правила грамматики Си тре- буют, чтобы f имел тело. Выполнение этого требования обеспечи- вает изолированная точка с запятой, называемая пустой инструкцией. Мы поставили точку с запятой на отдельной строке для большей наглядности.
    заметим, что если ввод не содержит ни одного символа, то при первом же обращении к getchar условие в while или не будет вы- полнено и программа выдаст нуль, что и будет правильным результатом.
    Это важно. Одно из привлекательных свойств и for состоит в том, что условие проверяется до того, как выполняется тело цикла. Если ничего делать не надо, то ничего делаться и не будет, пусть тело цикла не выполнится ни разу. Программа должна вести себя корректно и при нулевом количестве вводимых символов. Само устройство циклов while и
    уверенность в правильном поведении програм- мы в случае граничных условий. '
    1.5.3. Подсчет строк
    Следующая программа подсчитывает строки. Как упоминалось выше,
    стандартная библиотека обеспечивает такую модель ввода-вывода, при которой входной текстовый поток состоит из последовательности строк,
    каждая из которых заканчивается символом новой строки. Следователь- но, подсчет строк сводится к подсчету числа символов новой строки.
    /* подсчет строк входного потока */
    main()
    {
    int с,
    = 0;
    while
    =
    != EOF)
    if (c == V)
    nl);
    }
    Тело цикла теперь образует инструкция под контролем которой на- ходится увеличение счетчика nl на единицу. Инструкция if проверяет условие в скобках и, если оно истинно, выполняет следующую за ним ин- струкцию (или группу инструкций, заключенную в фигурные скобки).
    Мы опять делаем отступы в тексте программы, чтобы показать, что чем управляется.

    Ввод-вывод символов 35
    Двойной знак равенства в языке Си обозначает оператор "равно" (он аналогичен оператору = в Паскале и . EQ. в
    Удваивание знака =
    в операторе проверки на равенство сделано для того, чтобы отличить его от единичного =, используемого в Си для обозначения присваивания. Пре- дупреждаем: начинающие программировать на Си иногда пишут а име- ют в виду ==. Как мы увидим в главе 2, в этом случае результатом будет обычно вполне допустимое по форме выражение, на компилятор не выдаст никаких предупреждающих
    Символ, заключенный в одиночные кавычки, представляет собой це- лое значение, равное коду этого символа (в кодировке, принятой на дан- ной машине). Это так называемая символьная константа. Существует и другой способ для написания маленьких целых значений. Например, '
    есть символьная константа; в наборе символов ее значение равня- ется 65 - внутреннему представлению символа А.
    роли кон- станты предпочтительнее, чем 65, поскольку смысл первой записи более очевиден, и она не зависит от конкретного способа кодировки символов.
    Эскейп-последовательности, используемые в строковых константах,
    допускаются также и в символьных константах.
    обозначает код символа новой строки, который в ASCII равен
    Следует обратить осо- бое внимание на то, что '
    обозначает один символ (код которого в вы- ражении рассматривается как целое значение), в то время как
    - стро- ковая константа, в которой чисто случайно указан один символ. Более подробно различие между символьными и строковыми константами раз- бирается в главе 2.
    Упражнение
    Напишите программу для подсчета пробелов, табуляций и новых строк.
    Упражнение 1.9. Напишите программу, копирующую символы ввода в выходной поток и заменяющую стоящие подряд пробелы один пробел.
    Упражнение 1.10. Напишите программу, копирующую вводимые символы в выходной поток с заменой символа табуляции на \t, символа забоя на и каждой обратной наклонной черты на \\. Это сделает видимыми все символы табуляции и забоя.
    1.5.4. Подсчет слов
    Четвертая из нашей серии полезных программ подсчитывает строки,
    слова и символы, причем под словом здесь имеется в виду любая строка '
    компиляторы, как выдают о возможной ошиб- ке. Примеч. ред.

    36 __ _ Глава
    Обзор языка
    символов, не содержащая в себе пробелов, табуляций и символов новой строки. Эта программа является упрощенной версией программы систе- мы UNIX.
    IN 1 /* внутри слова */
    OUT 0 /* вне слова */
    /.* подсчет строк, слов и символов */
    {
    int с,
    nw, nc, state;
    state = OUT;
    nl
    = nw = nc = 0;
    while ((c =
    EOF) {
    ++nc;
    if (c ==
    ' n ' )
    if (c ==
    c ==
    i с ==
    )
    state = OUT;
    else if (state == OUT) {
    state = IN;
    ++nw;
    %d nl, nw, nc);
    Каждый раз, встречая первый символ слова, программа изменяет зна- чение счетчика слов на Переменная state фиксирует текущее состоя- ние - находимся мы внутри или вне слова. Вначале ей присваивается зна,- чение OUT, что соответствует состоянию "вне слова". Мы предпочитаем пользоваться именованными константами IN и OUT, а не собственно зна- чениями 1 и чтобы сделать программу более понятной. В такой малень- кой программе этот прием что дает, но в большой программе увели- чение ее ясности окупает незначительные дополнительные усилия, по- траченные на то, чтобы писать программу в таком стиле с самого начала.
    Вы обнаружите, что большие изменения гораздо легче вносить в те про- граммы, в которых магические числа встречаются только в виде имено- ванных констант.

    1.5. Ввод-вывод символов 37
    Строка nl
    = пс = 0;
    устанавливает все три переменные в нуль. Такая запись не является ка- кой-то особой конструкцией и допустима что присваивание есть выражение со своим собственным значением, а операции присваивания выполняются справа налево. Указанная строка эквивалентна nl = (nw
    =
    (пс =
    Оператор ! ! означает ИЛИ, так что строка
    (с ==
    ' '
    с ==
    ::
    с ==
    читается как "если с есть пробел, или с есть новая строка, или с есть табу- ляция". (Напомним, что видимая эскейп-последовательность \t обозна- чает символ табуляции.) Существует также оператор &&, означающий И.
    Его приоритет чем приоритет
    Выражения, связанные операто- рами && или вычисляются слева направо; при этом гарантируется, что вычисления сразу прервутся, как только будет установлена истинность или ложность условия. Если с есть то дальше проверять, является значение с символом новой строки или же не нужно. В этом частном случае данный способ вычислений не столь важен, но он имеет значение в более сложных ситуациях, которые мы вскоре рассмотрим.
    В примере также встречается слово else, которое указывает на альтерна- тивные действия, выполняемые в случае, когда условие, указанное в не является истинным. В общем виде условная инструкция записывается так:
    if (выражение)
    else
    В конструкции выполняется одна и только одна из двух инструк- ций. Если выражение истинно, то выполняется если нет,
    то -
    Каждая из этих двух инструкций представляет собой либо одну инструкцию, либо несколько, заключенных в фигурные скобки.
    В нашей программе после else стоит инструкция управляющая двумя такими инструкциями.
    Упражнение 1.11. Как протестировать программу подсчета слов? Какой ввод вероятнее всего обнаружит ошибки, если они были допущены?
    Упражнение 1.12. Напишите программу, которая печатает содержимое своего ввода, помещая по одному слову на каждой строке.

    38 _ Глава
    Обзор языка
    1.6. Массивы
    А теперь напишем программу, подсчитывающую по отдельности каж- дую цифру, символы-разделители (пробелы, табуляции и ки) и все другие символы. Это несколько искусственная программа, но она позволит нам в одном примере продемонстрировать еще несколько возможностей языка Си. Имеется двенадцать категорий вводимых сим- волов. Удобно все десять счетчиков цифр хранить в массиве, а не в виде десяти отдельных переменных. Вот один из вариантов этой программы:
    ttinclude
    /* подсчет цифр, символов-разделителей и прочих символов */
    {
    int с, i, nwhite, nother;
    int nwhite = nother = 0;
    for (i = 0; i < 10; ++i)
    ndigit[i]= 0;
    while ((c
    EOF)
    if (c >=
    && с <=
    )
    ++ndigit[c -
    ];
    else if (c == ' ' !! с ==
    !! с ==
    ++nwhite;
    else for i < 10; ++i)
    символы-разделители =
    прочие =
    nwhite, nother);
    В результате выполнения этой программы будет напечатан следующий результат:
    цифры = 9 3 0 0 0 0 0 0 0 1 , символы-разделители = 123, прочие = 345
    int

    Массивы 39
    объявляет ndigit массивом из 10 значений типа int. В Си элементы мас- сива всегда нумеруются начиная с нуля, так что элементами этого масси- ва будут ndigit[0], ndigit[1]
    учитывается в
    (при инициализации и печати массива).
    Индексом может быть любое целое выражение, образуемое целыми переменными (например и целыми константами.
    Приведенная программа опирается на определенные свойства кодиров- ки цифр.
    проверка if (с >=
    && с <=
    определяет, является ли находящийся в с символ цифрой. Если это так, то с - есть числовое значение цифры. Сказанное справедливо только в том слу- чае, если для ряда значений '
    каждое следующее значение на 1
    больше предыдущего. К счастью, это правило соблюдается во всех набо- рах символов.
    По определению, значения типа char являются просто малыми целы- ми, так что переменные и константы типа char в арифметических выра- жениях идентичны значениям типа int. Это и естественно, и удобно; на- пример,
    есть целое выражение с возможными значениями от 0 до 9,
    которые соответствуют символам от ' до '
    хранящимся в перемен- ной с. Таким образом, значение данного выражения является правиль- ным индексом для массива ndigit.
    Следующий фрагмент определяет, является символ цифрой,
    разделителем или чем-нибудь иным.
    if (с >=
    && с <=
    -
    ];
    else if (с ==' ' с ==
    ==
    )
    else
    ++nother
    Конструкция вида if else if else

    1   2   3   4   5   6   7   8   9   ...   28


    написать администратору сайта