Рекомендуется всегда инициализировать объявления переменных на внешнем уровне в файлах, которые предназначены для помещения в библиотеку. Это повышают вероятность выявления случаев нежелательного совпадения имен внешних переменных в библиотечном файле и пользовательской программе. Если переменная в программе пользователя также инициализирована, то компоновщик обнаружит два инициализированных объявления одной и той же глобальной переменной и сообщит об ошибке.
Возможно наличие в одном исходном файле на внешнем уровне нескольких объявлений переменной с одним и тем же именем. Следующая таблица позволяет определить реакцию компилятора языка Си в различных ситуациях изменения спецификации класса памяти в объявлении. Слово "пусто" в таблице означает ситуацию отсутствия спецификации класса памяти. Очевидно, что компилятор СП MSC строже ограничивает возможность переопределения класса памяти переменной.
Класс 1 | Класс 2 | СП TC | СП MSC |
extern | static | static | static |
static | пусто | static | ошибка |
static | extern | static | static |
пусто | static | static | ошибка |
/* объявление i, ссылающееся на данное ниже определение i */
extern int i;
main()
{
i = i + 1;
printf("%d\n", i); /* значение i равно 4 */
next();
}
int i = 3; /* определение i */
next()
{
printf("%d\n", i); /* значение i равно 5 */
other());
}
/* ИСХОДНЫЙ ФАЙЛ 2 */
/* объявление i, ссылающееся на определение i в первом исходном файле */
extern int i;
other()
{
i = i + 1;
printf("%d\n", i); /* значение i равно 6 */
}
Два исходных файла в совокупности содержат три внешних объявления i. Только в одном объявлении содержится инициализация:
int i = 3; — глобальная переменная i определена с начальным значением 3.
Самое первое объявление extern в первом исходном файле делает глобальную переменную i доступной прежде ее определения в файле. Без этого объявления функция main не могла бы использовать глобальную переменную i. Объявление переменной i во втором исходном файле делает глобальную переменную i доступной во втором исходном файле.
Все три функции выполняют одно и то же действие: увеличивают i на 1 и печатают полученное значение. Значения распечатываются с помощью стандартной библиотечной функции printf. Печатаются значения 4, 5 и 6.
Если бы переменная i не была инициализирована ни в одном из объявлений, она была оы неявно инициализирована нулевым значением при компоновке. В этом случае программа напечатала бы значения 1, 2 и 3.
Объявление переменной на внутреннем уровне
Любая из четырех спецификаций класса памяти может быть использована для объявления переменной на внутреннем уровне. Если спецификация класса памяти опущена в объявлении переменной на внутреннем уровне, то подразумевается класс памяти auto. Как правило, ключевое слово auto опускается. Понятия объявления и определения для переменных внутреннего уровня совпадают, если только в объявлении не задана спецификация класса памяти extern.
Спецификация класса памяти auto объявляет переменную с локальным временем жизни. Область действия переменной распространяется на блок, в котором она объявлена, (и на все вложенные в него блоки). Переменные класса памяти auto автоматически не инициализируются, поэтому в случае отсутствия инициализации в объявлении значение переменной класса памяти auto считается неопределенным. Память под переменные класса памяти auto отводится в стеке.
Спецификация класса памяти register требует, чтобы компилятор языка Си выделил переменной память в регистре микропроцессора, если это возможно. Использование регистровой памяти обычно ускоряет доступ к переменной и уменьшает размер выполняемого кода программы. Переменные, объявленные с классом памяти register, имеют ту же самую область действия, что и переменные auto.