Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Тема 3.2 (ч.1). Команды и данные. Абстракция да...docx
Скачиваний:
0
Добавлен:
01.05.2025
Размер:
624 Кб
Скачать
☆

3.2.3.10 Адресные выражения

Адресными выражениями называются выражения с операндами-указате­лями.

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

При записи адресных выражений применяются следующие правила:

  1. При работе с указателями ис­пользуются одноместные операции: «&» (получение адреса) – для получения адреса данного, и «*» (разыменование адреса) – для обращения к данному по его адресу.

  2. Указателю не может быть присвоена величина, не являющаяся адресом.

  3. Нельзя присваивать указателю одного типа значение, являющееся адресом данного другого типа. И дело не в том, что указатель одного типа не может вместить адреса данного иного типа, – адреса разнотипных данных имеют одну и ту же длину. Но такие операции смешения адресов запрещены, поскольку интерпретация данных компилятором зависит от типа указателя на них. Исключение составлет указатель типа void *. Тип данного, на который ссылается указатель ptr типа void *, неизвестен, и манипулировать данным *ptr непосредственно нельзя. Однако можно присвоить значение такого указателя другому типизированному указателю, или привести тип void * к любому иному типу (например, приведение типа указателя к символьному типу записывается так: (char *)ptr), или сравнить значение этого указателя с адресом какого-либо данного.

  4. Инкремент и декремент значения указателя эквивалентен смещению на 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.

  1. Прибавление к некоторому указателю положительного целого числа n дает указатель, который ссылается на элемент памяти, смещенный относительно исходного вправо на n ∙ sizeof(тип указателя). Для рассмотренного выше примера результат выполнения операции pZ + 2 будет указывать на данное X с адресом 0012F488.

  2. По той же причине вычитание из некоторого указателя положительного целого числа n дает указатель, который ссылается на элемент памяти, смещенный относительно исходного влево на n ∙ sizeof(тип указателя). Для рассмотренного выше примерарезультат выполнения операции pX - 2 будет указывать на данное Z с адресом 0012F400.

  3. Разность двух указателей одного типа дает количество элементов памяти, расположенных между двумя операндами-адресами. Так, например для приведенного выше примера результат выполнения операции pZ - ptrX равен 2.

0012F400

0012F488

0012F484

RAM

4

Z=

X=

8

· · · · · · · · · · · · · · · · · · · · · · ·

6

Y=

Рисунок 3.2.13

  1. Указатели можно сравнивать между собой с помощью операций «= =», «!=», «<», «<=», «>», «>=». Естественно, что выполнение операции 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 естественно, что нужно следить за корректностью выполнения этой операции во избежание выхода за пределы массива