Керниган и Ритчи - Язык программирования Си (793773), страница 12
Текст из файла (страница 12)
А вот пользователям функции copy размеры копируемых строк известны(или они могут их узнать), поэтому дополнительный контроль здесь не нужен.Упражнение 1.16. Перепишите main предыдущей программы так, чтобы она могла печатать самую длиннуюстроку без каких-либо ограничений на ее размер.Упражнение 1.17. Напишите программу печати всех вводимых строк, содержащих более 80 символов.Упражнение 1.18. Напишите программу, которая будет в каждой вводимой строке заменять стоящие подрядсимволы пробелов и табуляций на один пробел и удалять пустые строки.Упражнение 1.19.
Напишите функцию reverse(s), размещающую символы в строке s в обратном порядке.Примените ее при написании программы, которая каждую вводимую строку располагает в обратном порядке.1.10. Внешние переменные и область видимостиПеременные line, longest и прочие принадлежат только функции main, или, как говорят, локальны в ней.Поскольку они объявлены внутри main, никакие другие функции прямо к ним обращаться не могут. То жеверно и применительно к переменным других функций. Например, i в getline не имеет никакогоотношения к i в copy.
Каждая локальная переменная функции возникает только в момент обращения к этойфункции и исчезает после выхода из нее. Вот почему такие переменные, следуя терминологии других языков,называют автоматическими. (В главе 4 обсуждается класс памяти static, который позволяет локальнымпеременным сохранять свои значения в промежутках между вызовами.)Так как автоматические переменные образуются и исчезают одновременно с входом в функцию и выходом изнее, они не сохраняют своих значений от вызова к вызову и должны устанавливаться заново при каждомновом обращении к функции.
Если этого не делать, они будут содержать "мусор".В качестве альтернативы автоматическим переменным можно определить внешние переменные, к которымразрешается обращаться по их именам из любой функции. (Этот механизм аналогичен области COMMON вФортране и определениям переменных в самом внешнем блоке в Паскале.) Так как внешние переменныедоступны повсеместно, их можно использовать вместо аргументов для связи между функциями по данным.Кроме того, поскольку внешние переменные существуют постоянно, а не возникают и исчезают на периодвыполнения функции, свои значения они сохраняют и после возврата из функций, их установивших.Внешняя переменная должна быть определена, причем только один раз, вне текста любой функции; в этомслучае ей будет выделена память.
Она должна быть объявлена во всех функциях, которые хотят еюпользоваться. Объявление содержит сведения о типе переменной. Объявление может быть явным, в видеинструкции extern, или неявным, когда нужная информация получается из контекста. Чтобыконкретизировать сказанное, перепишем программу печати самой длинной строки с использованием line,longest и max в качестве внешних переменных. Это потребует изменений в вызовах, объявлениях и телахвсех трех функций.#include <stdio.h>#define MAXLINE 1000 /* максимальный размер вводимой строки */int max; /* длина максимальной из просмотренных строк */char line[MAXLINE]; /* текущая строка */char longest[MAXLINE]; /* самая длинная строка */int getline(void);void copy(void);/* печать самой длинной строки; специализированная версия */main (){int len;extern int max;extern char longest[];max = 0;while ((len = getline()) > 0)if (len > max) {max = len;copy();}if (max > 0) /* была хотя бы одна строка */printf("%s", longest);return 0;}/* getline: специализированная версия */int getline(void){int c, i;extern char line[];for (i=0; i < MAXLINE-1 && (c=getchar()) != EOF && с != '\n'; ++i)line[i] = c;if(c == '\n') {line[i]= c;++i;}line[i] = '\0';return i;}/* copy: специализированная версия */void copy (void){int i;extern char line[], longest[];i = 0;while ((longest[i] = line[i]) != '\0')++i;}Внешние переменные для main, getline и copy определяются в начале нашего примера, где имприсваивается тип и выделяется память.
Определения внешних переменных синтаксически ничем неотличаются от определения локальных переменных, но поскольку они расположены вне функций, этипеременные считаются внешними. Чтобы функция могла пользоваться внешней переменной, ей нужнопрежде всего сообщить имя соответствующей переменной. Это можно сделать, например, задав объявлениеextern, которое по виду отличается от объявления внешней переменной только тем, что оно начинается сключевого слова extern.В некоторых случаях объявление extern можно опустить.
Если определение внешней переменной висходном файле расположено выше функции, где она используется, то в объявлении extern нетнеобходимости. Таким образом, в main, getline и copy объявления extern избыточны. Обычноопределения внешних переменных располагают в начале исходного файла, и все объявления extern дляних опускают.Если же программа расположена в нескольких исходных файлах и внешняя переменная определена в файле1, а используется в файле 2 и файле З, то объявления extern в файле 2 и файле З обязательны, посколькунеобходимо указать, что во всех трех файлах функции обращаются к одной и той же внешней переменной.
Напрактике обычно удобно собрать все объявления внешних переменных и функций в отдельный файл,называемый заголовочным (header-файлом), и помещать его с помощью #include в начало каждогоисходного файла. В именах header-файлов по общей договоренности используется суффикс .h. В этих файлах,в частности в <stdio.h>, описываются также функции стандартной библиотеки. Более подробно озаголовочных файлах говорится в главе 4, а применительно к стандартной библиотеке — в главе 7 иприложении В.Так как специализированные версии getline и сору не имеют аргументов, на первый взгляд кажется, чтологично их прототипы задать в виде getline() и copy().
Но из соображений совместимости со старымиСи-программами стандарт рассматривает пустой список как сигнал к тому, чтобы выключить все проверки насоответствие аргументов. Поэтому, когда нужно сохранить контроль и явно указать отсутствие аргументов,следует пользоваться словом void. Мы вернемся к этой проблеме в главе 4.Заметим, что по отношению к внешним переменным в этом параграфе мы очень аккуратно используемпонятия определение и объявление. "Определение" располагается в месте, где переменная создается и ейотводится память; "объявление" помещается там, где фиксируется природа переменной, но никакой памятидля нее не отводится.Следует отметить тенденцию все переменные делать внешними.
Дело в том, что, как может показаться напервый взгляд, это приводит к упрощению связей — ведь списки аргументов становятся короче, апеременные доступны везде, где они нужны; однако они оказываются доступными и там, где не нужны. Такчто чрезмерный упор на внешние переменные чреват большими опасностями — он приводит к созданиюпрограмм, в которых связи по данным не очевидны, поскольку переменные могут неожиданным и дажетаинственным способом изменяться.
Кроме того, такая программа с трудом поддается модификациям.Вторая версия программы поиска самой длинной строки хуже, чем первая, отчасти по этим причинам, аотчасти из-за нарушения общности двух полезных функций, вызванного тем, что в них вписаны именаконкретных переменных, с которыми они оперируют.Итак, мы рассмотрели то, что можно было бы назвать ядром Си. Описанных "кирпичиков" достаточно, чтобысоздавать полезные программы значительных размеров, и было бы чудесно, если бы вы, прервав чтение,посвятили этому какое-то время. В следующих упражнениях мы предлагаем вам создать несколько болеесложные программы, чем рассмотренные выше.Упражнение 1.20. Напишите программу detab, заменяющую символы табуляции во вводимом тексте нужнымчислом пробелов (до следующего "стопа" табуляции).
Предполагается, что "стопы" табуляции расставлены нафиксированном расстоянии друг от друга, скажем, через n позиций. Как лучше задавать n — в виде значенияпеременной или в виде именованной константы?Упражнение 1.21. Напишите программу entab, заменяющую строки из пробелов минимальным числомтабуляций и пробелов таким образом, чтобы вид напечатанного текста не изменился.
Используйте те же"стопы" табуляции, что и в detab. В случае, когда для выхода на очередной "стоп" годится один пробел, чтолучше — пробел или табуляция?Упражнение 1.22. Напишите программу, печатающую символы входного потока так, чтобы строки текста невыходили правее n-й позиции.














