Язык С

       

Область действия: внешние переменные


Переменные в MAIN(LINE, SAVE и т.д.) являются внутренни- ми или локальными по отношению к функции MAIN, потому что они описаны внутри MAIN и никакая другая функция не имеет к ним прямого доступа. Это же верно и относительно переменных в других функциях; например, переменная I в функции GETLINE никак не связана с I в COPY. Каждая локальная переменная су- ществует только тогда, когда произошло обращение к соответс- твующей функции, и исчезает, как только закончится выполне- ние этой функции. По этой причине такие переменные, следуя терминологии других языков, обычно называют автоматическими. Мы впредь будем использовать термин автоматические при ссыл- ке на эти динамические локальные переменные. /в главе 4 об- суждается класс статической памяти, когда локальные перемен- ные все же оказываются в состоянии сохранить свои значения между обращениями к функциям/. Поскольку автоматические переменные появляются и исчеза- ют вместе с обращением к функции, они не сохраняют своих значений в промежутке от одного вызова до другого, в силу чего им при каждом входе нужно явно присваивать значения. Если этого не сделать, то они будут содержать мусор. В качестве альтернативы к автоматическим переменным мож- но определить переменные, которые будут внешними для всех функций, т.е. Глобальными переменными, к которым может обра- титься по имени любая функция, которая пожелает это сделать. (этот механизм весьма сходен с "COMMON" в фортране и "EXTERNAL" в PL/1). Так как внешние переменные доступны всю- ду, их можно использовать вместо списка аргументов для пере- дачи данных между функциями. Кроме того, поскольку внешние переменные существуют постоянно, а не появляются и исчезают вместе с вызываемыми функциями, они сохраняют свои значения и после того, как функции, присвоившие им эти значения, за- вершат свою работу. Внешняя переменная должна быть определена вне всех функ- ций; при этом ей выделяется фактическое место в памяти. Та- кая переменная должна быть также описана в каждой функции, которая собирается ее использовать; это можно сделать либо явным описанием EXTERN, либо неявным по контексту. Чтобы сделать обсуждение более конкретным, давайте перепишем прог- рамму поиска самой длинной строки, сделав LINE, SAVE и MAX внешними переменными. Это потребует изменения описаний и тел всех трех функций, а также обращений к ним.


#DEFINE MAXLINE 1000 /* MAX. INPUT LINE SIZE*/

CHAR LINE[MAXLINE]; /* INPUT LINE */ CHAR SAVE[MAXLINE];/* LONGEST LINE SAVED HERE*/ INT MAX;/* LENGTH OF LONGEST LINE SEEN SO FAR*/ MAIN() /*FIND LONGEST LINE; SPECIALIZED VERSION*/ { INT LEN; EXTERN INT MAX; EXTERN CHAR SAVE[]; MAX = 0;

WHILE ( (LEN = GETLINE()) > 0 ) IF ( LEN > MAX ) { MAX = LEN; COPY(); } IF ( MAX > 0 ) /* THERE WAS A LINE */ PRINTF( "%S", SAVE ); }

GETLINE() /* SPECIALIZED VERSION */ { INT C, I; EXTERN CHAR LINE[];

FOR (I = 0; I < MAXLINE-1

&& (C=GETCHAR()) !=EOF && C!='\N'; ++I) LINE[I] = C; ++I; } LINE[I] = '\0' RETURN(I) } COPY() /* SPECIALIZED VERSION */ { INT I; EXTERN CHAR LINE[], SAVE[];

I = 0; WHILE ((SAVE[I] = LINE[I]) !='\0') ++I; }

Внешние переменные для функций MAIN, GETLINE и COPY оп- ределены в первых строчках приведенного выше примера, кото- рыми указывается их тип и вызывается отведение для них памя- ти. синтаксически внешние описания точно такие же, как опи- сания, которые мы использовали ранее, но так как они распо- ложены вне функций, соответствующие переменные являются внешними. Чтобы функция могла использовать внешнюю переме- ную, ей надо сообщить ее имя. Один способ сделать это - включить в функцию описание EXTERN; это описание отличается от предыдущих только добавлением ключевого слова EXTERN.

В определенных ситуациях описание EXTERN может быть опу- щено: если внешнее определение переменной находится в том же исходном файле, раньше ее использования в некоторой конкрет- ной функции, то не обязательно включать описание EXTERN для этой переменной в саму функцию. Описания EXTERN в функциях MAIN, GETLINE и COPY являются, таким образом, излишними. Фактически, обычная практика заключается в помещении опреде- лений всех внешних переменных в начале исходного файла и последующем опускании всех описаний EXTERN. Если программа находится в нескольких исходных файлах, и некоторая переменная определена, скажем в файле 1, а исполь- зуется в файле 2, то чтобы связать эти два вхождения пере- менной, необходимо в файле 2 использовать описание EXTERN. Этот вопрос подробно обсуждается в главе 4. Вы должно быть заметили, что мы в этом разделе при ссыл- ке на внешние переменные очень аккуратно используем слова описание и определение. "Определение" относится к тому мес- ту, где переменная фактически заводится и ей выделяется па- мять; "описание" относится к тем местам, где указывается природа переменной, но никакой памяти не отводится. Между прочим, существует тенденция объявлять все, что ни попадется, внешними переменными, поскольку кажется, что это упрощает связи, - списки аргументов становятся короче и пе- ременные всегда присутствуют, когда бы вам они ни понадоби- лись. Но внешние переменные присутствуют и тогда, когда вы в них не нуждаетесь. Такой стиль программирования чреват опас- ностью, так как он приводит к программам, связи данных внут- ри которых не вполне очевидны. Переменные при этом могут из- меняться неожиданным и даже неумышленным образом, а програм- мы становится трудно модифицировать, когда возникает такая необходимость. Вторая версия программы поиска самой длинной строки уступает первой отчасти по этим причинам, а отчасти потому, что она лишила универсальности две весьма полезные функции, введя в них имена переменных, с которыми они будут манипулировать.

Упражнение 1-18

--------------- Проверка в операторе FOR функции GETLINE довольно неук- люжа. Перепишите программу таким образом, чтобы сделать эту проверку более ясной, но сохраните при этом то же самое по- ведение в конце файла и при переполнении буфера. Является ли это поведение самым разумным?




    Содержание раздела