Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
шпоры по ямп.docx
Скачиваний:
10
Добавлен:
01.04.2025
Размер:
162 Кб
Скачать
☆

13.Спецификаторы класса памяти.

Переменные

Объявление переменной задает имя и атрибуты переменной. Определение переменной, помимо этого, приводит к выделению для нее памяти. Кроме того, определение переменной задает её начальное значение (явно или неявно). Таким образом, не каждое объявление переменной является определением переменной. К объявлениям, которые не являются определением относятся объявления формальных параметров функций, а также объявления со спецификацией класса памяти extern, которые являются ссылкой на переменную, определённую в другом месте программы (см. ниже).

Объявления переменных в языке C++ имеют следующий синтаксис:

[<спецификация класса памяти>] <тип> <описатель> [= <инициализатор>] [, <описатель> [= <инициализатор>] ...];

В языке C++ имеется четыре спецификации класса памяти:

auto

register

static

extern

Спецификации класса памяти auto и register могут быть использованы только на внутреннем уровне.

1.1.1. Глобальные переменные

Переменная, объявленная на внешнем уровне, имеет глобальное время жизни. При отсутствии инициализатора такая переменная инициализируется нулевым значением. Область видимости переменной, определенной на внешнем уровне, распространяется от точки, где она определена, до конца исходного файла. Переменная недоступна выше своего определения в том же самом исходном файле. На другие исходные файлы программы область видимости переменной может быть распространена только в том случае, если ее определение не содержит спецификации класса памяти static.

Если в объявлении переменной задана спецификация класса памяти static, то в других исходных файлах могут быть определены другие переменные с тем же именем и любым классом памяти. Эти переменные никак не буду связаны между собой.

Спецификация класса памяти extern используется для объявления переменной, определенной где-то в другом месте программы. Такие объявления используются в случае, когда нужно распространить на данный исходный файл область видимости переменной, определенной в другом исходном файле на внешнем уровне. Область видимости переменной распространяется от места объявления до конца исходного файла. В объявлениях, которые используют спецификацию класса памяти extern, инициализация не допускается, так как они ссылаются на переменные, значения которых определены в другом месте.

1.1.2. Локальные переменные

Переменная, объявленная на внутреннем уровне, доступна только в том, блоке в котором она объявлена,независимо от класса памяти. По умолчанию она имеет класс памяти auto. Переменные этого класса размещаются в стеке. Переменные класса памяти auto автоматически не инициализируются, поэтому в случае отсутствия инициализации в объявлении значение переменной класса памяти auto считается неопределенным.

Спецификация класса памяти register требует, чтобы переменной была выделена память в регистре, если это возможно. Т.к. работа с регистрами происходит быстрее, спецификация класса памяти register обычно используется для переменных, к которым предполагается обращаться очень часто.

Для каждого рекурсивного входа в блок порождается новый набор переменных класса памяти auto иregister. При этом каждый раз производится инициализация переменных, в объявлении которых заданы инициализаторы.

Если переменная, объявленная на внутреннем уровне, имеет спецификацию памяти static, то область видимости остается прежней, а время жизни становится глобальным. В отличие от переменных класса памятиauto, переменные, объявленные со спецификацией класса памяти static, сохраняют свое значение при выходе из блока. Переменные класса памяти static могут быть инициализированы константным выражением. Если явной инициализации нет, то переменная класса памяти static автоматически инициализируется нулевым значением. Инициализация выполняется один раз во время компиляции и не повторяется при каждом входе в бока. Все рекурсивные вызовы данного бока будут разделять единственный экземпляр переменной класса памяти static.

Переменная, объявленная со спецификацией класса памяти extern, является ссылкой на переменную с тем же самым именем, определенную на внешнем уровне в любом исходном файле программы. Цель внутреннего объявления extern состоит в том, чтобы сделать определение переменной доступным именно внутри данного блока.

Файл 1 

Файл 2 

int a, b;

static int c;

extern int d = 10;

int f(int x)

{ int y;

static int z;

static int c;

...

}

// Глобальные переменные

// На переменную с нельзя будет

// сослаться из другого файла

// Нельзя использовать инициализатор с extern

// Переменная класса auto.

// Локальные время жизни и область видимости.

// Локальная переменная класса static.

// Имеет глобальное время жизни.

// Локальная статическая переменная c

// скрывает глобальную переменную c

extern int a;

int d = 10;

auto int e;

register int e;

int g(int x)

{ extern int b;

register int w;

...

}

// Ссылка на переменную а из файла 1

// Нельзя использовать auto и register

// на внешнем уровне

// Ссылка на переменную b из файла 1,

// переменная будет доступна только в функции g

// Переменная класса register

1.1.3. Сокрытие имён

Объявление имени в блоке может скрыть объявление этого имени в охватывающем блоке или глобальное имя. То есть имя может быть замещено внутри блока и будет ссылаться там на другую сущность. После выхода из блока имя восстанавливает свой прежний смысл. К скрытому глобальному имени можно обратиться с помощью операции разрешения области видимости ::. Скрытое имя члена класса можно использовать, квалифицировав его именем класса. Скрытое глобальное имя можно использовать, если квалифицировать его унарной операцией разрешения области видимости.

int x;

void f()

{ double x = 0;

// Глобальная переменная х скрыта

::x = 2;

// Присваивание глобальной переменной х

x = 2.5;

// Присваивание локальной переменной х

}

int f(int x) { ... }

class X

{ public:

static int f() { ... }

};

int ff()

{ return X::f(); }

// Вызов функции f класса Х, а не глобальной функции f

Не существует способа обращений к скрытой локальной переменной.

1.1.4. Объявления в условиях и цикле for

Во избежание случайного неправильного использования переменных их лучше вводить в наименьшей возможной области видимости. В частности, локальную переменную лучше объявлять в тот момент, когда ей надо присвоить значение. В этом случае исключаются попытки использования переменной до момента её инициализации.

Одним из самых элегантных применений этих идей является объявление переменной в условии. Рассмотрим пример.

if (double d = f(x))

y /= d;

Область видимости переменной d простирается от точки её объявления до конца оператора, контролируемого условием. Если бы в инструкции if была ветвь else, то областью видимости переменной dбыли бы обе ветви.

Очевидной и традиционной альтернативой является объявление переменной до условия, но в этом случае область видимости началась бы до мести использования переменной и продолжалась бы после завершения её «сознательной» жизни.

double d;

...

d2 = d;

...

if (d = f(x))

y /= d;

...

// Внимание!!!

d = 2.0;

// Два несвязанных использования переменной d

Объявление переменных в условиях, кроме того, что даёт логические преимущества, приводит также к более компактному исходному коду.

Объявление в условии должно объявлять и инициализировать единственную переменную или константу.

Переменную можно также объявить в инициализирующей части оператора for. В этом случае область видимости переменной (или переменных) простирается до конца оператора.

void f(int x[], int n)

{ for (int i = 0; i < n; i++) x[i] = i * i; }

Если требуется узнать значение индекса после выхода из цикла, переменную надо объявить вне его.

1.2. Функции

Объявление функции (прототип) задает её имя, тип возвращаемого значения и атрибуты её формальных параметров. Объявления функций в языке C++ имеют следующий синтаксис:

[<спецификация класса памяти>] <тип> <имя> (<список формальных параметров>);

Определение функции специфицирует тело функции, которое представляет собой составной оператор, содержащий объявления и операторы. Определение функции также задает её имя, тип возвращаемого значения и атрибуты её формальных параметров. Определение функции имеет следующий синтаксис:

[<спецификация класса памяти>] <тип> <имя> (<список формальных параметров>)

{ <тело функции> }

Функции имеют глобальное время жизни.

Определение функции может быть задано только на внешнем уровне. Область видимости функции распространяется от определения до конца файла. Чтобы использовать вызов функции выше её определения, нужно написать объявление функции (прототип). Прототип некоторой функции может быть расположен на внешнем уровне (тогда вызов этой функции будет возможен из любой функции исходного файла) или же на внутреннем уровне (тогда вызов данной функции будет возможен только из блока, в котором находится прототип).

Для использования функции, расположенной в другом исходном файле, также необходимо использовать прототип.

Если функция определена со спецификацией класса памяти static, ее использование в других исходных файлах программы невозможно.

Встраиваемые функции компонуются внутренним образом (т.е. не могут быть использованы в других файлах программы), если только явным образом не указана внешняя компоновка с помощью ключевого слова extern.