- •Данных. Составные структуры данных
- •3.2.1 Общие положения
- •3.2.1.1 Алфавит языка
- •3.2.1.2 Формат программы
- •3.2.1.3. Комментарии
- •// Пример однострочного комментария
- •3.2.2.2 Типы данных
- •3.2.1.4 Зарезервированные слова
- •3.2.2 Данные
- •3.2.2.1 Идентификаторы данных
- •3.2.2.3 Постоянные
- •3.2.2.4 Переменные
- •3.2.2.5 Массивы
- •3.2.2.6 Структуры
- •3.2.2.7 Объединения
- •Масив X:
- •Адреса элементу:
- •Масив y:
- •3.2.2.8 Перечисления
- •3.2.2.9 Указатели
- •Int Hour;
- •Int Press;
- •3.2.2.9.1 Динамическое выделения памяти для хранения данных
- •3.2.2.9.2 Динамическое освобождение памяти от хранимых данных
- •3.2.2.9.3 Ссылка
- •3.2.2.10 Множества
- •3.2.3 Выражения
- •3.2.3.1 Арифметические операции
- •Char int float | signed unsigned | short (базовый) long
- •3.2.3.2 Битовые операции
- •3.2.3.2 Логические операции
- •3.2.3.4 Операции отношения
- •3.2.3.5 Операция присваивания
- •3.2.3.6 Специальные операции
- •3.2.3.7 Элементарные функции
- •3.8 Приведение типов данных
- •3.2.3.9 Приоритеты операций
- •3.2.3.10 Адресные выражения
3.2.3.10 Адресные выражения
Адресными выражениями называются выражения с операндами-указателями.
Над указателями могут быть выполнены действия целочисленной арифметики, которые всегда выполняются в соответствии с типом указателя (т.е. типом данного, на которое ссылается указатель).
При записи адресных выражений применяются следующие правила:
При работе с указателями используются одноместные операции: «&» (получение адреса) – для получения адреса данного, и «*» (разыменование адреса) – для обращения к данному по его адресу.
Указателю не может быть присвоена величина, не являющаяся адресом.
Нельзя присваивать указателю одного типа значение, являющееся адресом данного другого типа. И дело не в том, что указатель одного типа не может вместить адреса данного иного типа, – адреса разнотипных данных имеют одну и ту же длину. Но такие операции смешения адресов запрещены, поскольку интерпретация данных компилятором зависит от типа указателя на них. Исключение составлет указатель типа void *. Тип данного, на который ссылается указатель ptr типа void *, неизвестен, и манипулировать данным *ptr непосредственно нельзя. Однако можно присвоить значение такого указателя другому типизированному указателю, или привести тип void * к любому иному типу (например, приведение типа указателя к символьному типу записывается так: (char *)ptr), или сравнить значение этого указателя с адресом какого-либо данного.
Инкремент и декремент значения указателя эквивалентен смещению на 1 фрагмент памяти; при этом размер этого фрагмента в байтах определяется типом указателя. Однако, следует учесть, что данные размещаются компилятором в стеке данных программы методом их «заталкивания» в стек в порядке описания и, начиная с наибольшего доступного программе адреса. Пусть, например, определены переменные X, Y, Z и их указатели, как это показано в следующем фрагменте программы (рисунок 3.2.12):
int X = 8, Y = 6, Z = 4;
int *pX = &X, *pY = &Y, *pZ = &Z;
Рисунок 3.2.12
Тогда выполнение операции ++pZ приведет к тому, что этот указатель станет ссылаться на переменную Y т.е. *++pZ = 6. Однако, значение самого адреса при этом не увеличивается, а уменьшается (!) (для приведенного примера с 0012F488 до 0012F484).
По той же причине инкремент значения указателя на элемент массива15 эквивалентен увеличению индекса массива на 1, а декремент значения указателя на элемент массива эквивалентен уменьшению индекса массива на 1.
Прибавление к некоторому указателю положительного целого числа n дает указатель, который ссылается на элемент памяти, смещенный относительно исходного вправо на n ∙ sizeof(тип указателя). Для рассмотренного выше примера результат выполнения операции pZ + 2 будет указывать на данное X с адресом 0012F488.
По той же причине вычитание из некоторого указателя положительного целого числа n дает указатель, который ссылается на элемент памяти, смещенный относительно исходного влево на n ∙ sizeof(тип указателя). Для рассмотренного выше примерарезультат выполнения операции pX - 2 будет указывать на данное Z с адресом 0012F400.
Разность двух указателей одного типа дает количество элементов памяти, расположенных между двумя операндами-адресами. Так, например для приведенного выше примера результат выполнения операции pZ - ptrX равен 2.
0012F400
0012F488
0012F484
RAM
4
Z=
X=
8
· · · · · · · · · · · · · · · · · · · · · · ·
6
Y=
Рисунок 3.2.13
Указатели можно сравнивать между собой с помощью операций «= =», «!=», «<», «<=», «>», «>=». Естественно, что выполнение операции pY > pX для приведенного выше примера дает результат false.
При работе с указателями особое внимание следует уделять порядку выполнения операций в выражении. Так, например, в выражении *pX++ сначала выполняется операция «*» разыменования указателя pX, а затем постфиксная операция «++», которая, являясь левоассоциативной, применяется к указателю pX и увеличивает его на единицу. А вот в выражении ++*pX выполняется увеличение на единицу данного, на которое ссылается указатель pX.
Следует также заметить, что применение операции присваивания «=» к указателям pX и pY (например, pY= pX) приводит к тому, что оба указателя будут ссылаться на один и тот же участок памяти. При этом данное, на который ранее ссылался указатель pY, будет безвозвратно «утеряно», поскольку обратиться к нему станет невозможно.
1 хотя подчеркивание и допускается к использованию в качестве буквы, но этот символ не рекомендуется использовать при написании пользовательских программ, поскольку многие системные программы используют его для именования разделяемых объектов
2 определения этим данным будут даны немного позже
3 в простейшем случае это ячейка памяти
4 включая и символы национального алфавита
5 под шаблон чего-либо, в том числе и структуры, память не отводится
6 в простейшем случае длиной 2 или 4 байта
7 символ «*» здесь называется декларатором указателя
8 операция sizeof (X) вычисляет размер X в байтах
9 Следует обратить внимание на правильное использование круглых скобок. Так, например, (*ptr) + 1 увеличивает на 1 значение, адресуемое указателем ptr, а *(ptr + 1) увеличивает на 1 сам указатель ptr и извлекает данное их следующего элемента памяти.
10 знак операции «обращение к элементу» набирается на клавиатуре как последовательность символов «-» и «>»
11 этот механизм будет рассмотрен немного позже
12 эти операции были рассмотрены ранее в разделе 3.2.2.9
13 эта операция будут рассмотрена далее в разделе 3.2.6
14 операция, имеющая три операнда
15 естественно, что нужно следить за корректностью выполнения этой операции во избежание выхода за пределы массива
