Программирование
Шрифт:
Спецификатор класса памяти register заставляет компилятор распределить память для переменной в регистре, если это возможно. Употребление регистровой памяти чаще всего приводит к сокращению времени доступа к переменной. Переменная, которая объявлена с классом памяти register, обладает той же областью видимости, что и переменная auto. Количество регистров, которые можно применить для значений переменных, не безгранично, так как не безграничны и возможности компьютера. В случае когда компилятор не обладает свободными регистрами, переменной выделяется память как для класса auto. Класс памяти register может указываться для переменных с типом int или указателей с размером, равным размеру int. Переменные, которые объявлены на внутреннем
14. Объявление переменной на внутреннем уровне с классом памяти static
В качестве примера рассмотрим объявление переменной i на внутреннем уровне с классом памяти static.
исходный файл filel.с
main
{
}
fun1
{static int i = 0; исходный файл file2.c fun2
{static int i = 0; }
fun3
{static int i = 0; }
В этом примере объявлены три различные переменные с классом памяти static, которые имеют одинаковые имена i. Все эти переменные обладают глобальным временем жизни, но видимы только в том блоке (функции), в котором они объявлены. Данные переменные можно применять для подсчета числа обращений к каждой из трех функций.
Переменные класса памяти static способны быть инициализированными константным выражением. Когда явной инициализации нет, то данной переменной присваивается нулевое значение. В случае инициализации константным адресным выражением 14б можно применять адреса любых внешних объектов, кроме адресов объектов, для которых класс памяти auto, так как их адрес не является константой и меняется при любом входе в блок. Инициализация осуществляется один раз при первом входе в блок.
Переменная, которая объявлена локально с классом памяти extern, служит ссылкой на переменную с таким же именем, определенную глобально в каком-либо из исходных файлов программы. Цель подобного объявления заключается в том, чтобы сделать определение переменной глобального уровня видимым внутри блока.
15. Объявление переменной, которая служит именем внешнего массива
Рассмотрим пример: объявление переменной i, которая служит именем внешнего массива длинных целых чисел, на локальном уровне.
исходный файл file1.c
main
{…
}
fun1
{extern long i[];…
}
/* исходный файл file2.c */
long i[MAX] = {0};
fun2
{…
}
fun3
{…
}
Объявление переменной i[] как extern в рассмотренном примере делает ее видимой в функции fun1. Определение данной переменной находится в файле file2.c на глобальном уровне и должно быть единственным. При этом объявлений с классом памяти extern может быть много.
Объявление переменной со спецификатором extern дает знать компилятору о том, что память для переменной не нужна, так как это выполнено где-то в другом месте программы.
В случае объявления переменных на глобальном уровне можно применить спецификатор класса памяти static или extern. Кроме того, можно объявлять переменные без указания класса памяти. Классы памяти auto и register для глобального объявления применять нельзя.
Объявление переменных на глобальном уровне представляет собой или определение переменных, или ссылки на определения, которые сделаны в другой части программы. Объявление глобальной переменной, инициализирующее данную переменную (явно
или неявно), служит определением переменной. Определение на глобальном уровне может быть задано в нескольких формах.1. С помощью класса памяти static. Данная переменная может быть инициализирована явно константным выражением либо по умолчанию нулевым значением. То есть объявления static int i = 0 и static int i одинаковы, и в том и в другом случае переменной i будет присвоено значение 0.
2. Переменная может быть объявлена без указания класса памяти, но с явной инициализацией. Подобной переменной по умолчанию присваивается класс памяти static. То есть объявления int i = 1 и static int i = 1 будут одинаковы.
16. Методы доступа к элементам массивов
Доступ к элементам массива может производиться двумя различными способами.
Первый способ связан с применением обычных индексных выражений в квадратных скобках, например: array[18] = 3 или array[i + 3] = 9. При данном способе доступа записываются два выражения. Второе выражение должно быть заключено в квадратные скобки. Одно из данных выражений должно являться указателем, а второе – выражением целого типа. Последовательность записи данных выражений может быть произвольной, однако в квадратных скобках следует записывать выражение, следующее вторым. Поэтому записи array[16] и 16[array] будут являться одинаковыми и обозначающими элемент массива с номером шестнадцать. Указатель, который используется в индексном выражении, не всегда является константой, которая указывает на какой-либо массив, это может быть и переменная. Например, после выполнения присваивания ptr = array доступ к шестнадцатому элементу массива можно получить, применяя указатель ptr в форме ptr[16] или 16[ptr].
Второй способ доступа к элементам массива связан с применением адресных выражений и операции раза-дресации в виде *(array+16) = 3 или *(array+i+2) = 7. При данном способе доступа адресное выражение соответствует адресу шестнадцатого элемента массива, тоже может быть записано различными способами: *(array+16) или *(16+array).
При работе на компьютере первый способ приводится ко второму, т. е. индексное выражение становится адресным. Для ранее рассмотренных примеров array[16] и 16[array] преобразуются в *(ar-ray+16).
Для доступа к начальному элементу массива, т. е. к элементу с нулевым индексом, можно применять просто значение указателя array или ptr. Любое из присваиваний
*array = 2;
array[0] = 2; *(array+0) = 2; *ptr = 2;
ptr[0] = 2;
*(ptr+0) = 2;
присваивает начальному элементу массива значение 2, но быстрее всего выполнятся присваивания *array = 2 и *ptr = 2, так как в них не требуется выполнять операции сложения.
17. Директивы препроцессора
Директивы препроцессора – это особые инструкции, которые записаны в тексте программы на СИ и выполнены до трансляции программы. Директивы препроцессора дают возможность изменить текст программы. Среди таких действий – замена некоторых лексем в тексте, вставка текста из другого файла, запрет на трансляцию части текста и т. п. Все директивы препроцессора должны начинаться со знака #. После директив препроцессора точки с запятой быть не должно.
Директива #include включает в программу содержимое определенного файла. Эта директива может быть представлена в двух формах:
#include «имя файла» #include <имя файла>
Имя файла должно соответствовать соглашениям операционной системы. Оно может включать в себя либо только имя файла, либо имя файла с предшествующим ему маршрутом. Когда имя файла указано в кавычках, то поиск файла производится по заданному маршруту, а при его отсутствии – в текущем каталоге. Когда имя файла задано в угловых скобках, поиск файла осуществляется в обычных директориях операционной системы, которые задаются командой PATH.