netlib.narod.ru | < Назад | Оглавление | Далее > |
Переменные 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); /* Печать самой длинной строки. Версия 2 */ 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 && c != '\n'; ++i) line[i] = c; if (c == '\n') { line[i] = c; ++i; } line[i] = '\0'; return i; } /* copy: копирует строку */ void copy(void) { int i; i = 0; while ((longest[i] = line[i]) != '\0') ++i; }
Внешние переменные для функций main, getline и copy определяются в первых строках нашего примера, где указывается их тип и выделяется память. Определения внешних переменных синтаксически ничем не отличаются от определения локальных переменных, но поскольку они расположены вне функций, эти переменные считаются внешними. Чтобы функция могла использовать внешнюю переменную, ей нужно прежде всего сообщить имя соответствующей переменной. Это можно сделать поместив в теле функции описание переменной, которое начинается с ключевого слова extern.
В некоторых случаях объявление extern можно опустить. Если определение внешней переменной в исходном файле расположено раньше функции, где она используется, то в объявлении extern нет необходимости. Таким образом, в функциях main, getline и copy объявления extern избыточны. Обычно определения внешних переменных располагают в начале исходного файла, и все объявления extern для них опускают.
Если же программа расположена в нескольких исходных файлах и внешняя переменная определена в файле-1, а используется в файле-2 и файле-3, то объявления extern в файле-2 и файле-3 обязательны, поскольку необходимо указать, что во всех трех файлах функции обращаются к одной и той же внешней переменной. На практике обычно удобно собрать все объявления внешних переменных и функций в отдельный файл, называемый заголовочным (header), и добавлять его с помощью директивы #include в начало каждого исходного файла. Для имен заголовочных файлов используется расширение .h. В этих файлах, в частности в stdio.h, описываются также функции стандартной библиотеки. Более подробно о заголовочных файлах говорится в главе 4, а о стандартной библиотеке — в главе 7 и приложении В.
Так как новые версии функций getline и copy не имеют аргументов, на первый взгляд кажется, что логично их прототипы задать в виде getline() и copy(). Но из соображений совместимости со старыми Си-программами стандарт рассматривает пустой список параметров как объявление функции в старом стиле и выключает проверку соответствия аргументов. Поэтому, когда нужно сохранить контроль и явно указать отсутствие аргументов, следует использовать ключевое слово void. Мы вернемся к этой проблеме в главе 4.
Обратите внимание, что по отношению к внешним переменным в этом параграфе мы очень аккуратно используем понятия определение и объявление. Определение располагается в месте, где переменная создается и ей отводится память; объявление помещается там, где объявляется тип переменной, но никакой памяти для нее не отводится.
Некоторые программисты стараются все переменные делать внешними. На первый взгляд может показаться, это приводит к упрощению связей — списки аргументов становятся короче, а переменные доступны везде, где они нужны. Однако переменные оказываются доступными и там, где они не нужны. Так что чрезмерная приверженностьвнешним переменным чревата большими опасностями — она приводит к созданию программ, в которых связи по данным не очевидны, поскольку переменные могут неожиданным и даже таинственным способом изменяться. Кроме того, такая программа с трудом поддается модификациям. Вторая версия программы поиска самой длинной строки хуже, чем первая, отчасти по этим причинам, а отчасти из-за нарушения универсальности двух полезных функций, вызванной тем, что в них вписаны имена конкретных переменных, с которыми они опреируют.
netlib.narod.ru | < Назад | Оглавление | Далее > |