- •Структуры и алгоритмы обработки данных Введение
- •1. Структуры данных
- •1.1 Уровни структур данных
- •1.2 Классификация структур данных
- •1.3. Информация и ее представление в памяти эвм
- •2. Простые структуры данных
- •2.1. Понятие о типах данных
- •2.2. Перечисляемый тип данных
- •2.3. Стандартные типы данных
- •2.3.1. Целочисленные типы
- •2.3.2. Вещественные числа
- •2.3.3. Представление и структуры хранения логической информации
- •2.4. Указатели
- •2.4.1. Назначение и смысл указателей
- •2.4.2 Операции с адресами
- •2.4.3 Указатели на указатели
- •2.5. Алгоритмы обработки простых структур данных
- •3. Линейные статические структуры данных
- •3.1 Массивы
- •3.2. Динамические массивы
- •3.3. Многомерные массивы
- •3.4. Связь массивов с указателями
- •3.5. Строки
- •3.6. Массивы указателей
- •3.7. Интерпретация составных описателей
- •3.8 Алгоритмы обработки статических линейных струткур
- •4. Ссылки
- •5. Интегральные типы данных (структуры, битовые поля, объединения)
- •5.1. Структуры
- •5.2. Битовые поля
- •5.3. Объединения
- •6. Файлы
2.4.2 Операции с адресами
Указатели могут использоваться в выражениях. Если y – указатель на целое значение, т.е. имело место объявление
int *y;
то *y может, как уже было рассмотрено в §2.4.1, появляться там же, где и любая другая переменная, не являющаяся указателем.
Вполне допустимы выражения:
*y = 7; // Запись числа 7 по адресу y
*x*= 5; // Увеличение значения по адресу x в 5 раз
(*z)++; // Увеличение на 1 значения по адресу z
Скобки в последнем выражении необходимы, т.к. операции * и ++ имеют одинаковый приоритет и выполняются справа налево. Если *z = 5, то после операции (*z)++ *z ~ 6, а *z++ только увеличит сам адрес, хранящийся в указателе z.
int *p; // Указатель на тип int
int i = 33;
p = &i;
*p = *p + 1; // i ~ 34;
Указатели можно использовать как операнды в арифметических операциях:
y++;
Унарная операция ++ увеличивает его значение, теперь оно является адресом следующего элемента. Указатели и целые числа можно складывать. Конструкция вида
y + n
задает адрес n-го объекта, адресуемого указателем y. Это справедливо для объектов любых стандартных типов (char, int и т.д.), транслятор будет масштабировать приращение адреса в соответствии с типом, определённым из соответствующего объявления. Арифметические операции, выполняемые над указателем и целым значением, имеют осмысленный результат, если указатель адресует непрерывный блок памяти, хранящий множество элементов одного типа.
Один указатель может быть вычтен из другого, если они указывают на один и тот же тип данных. Разность между двумя указателями преобразуется к знаковому целому значению путем деления разности на длину типа, данные которого адресуются указателями. Результат представляет число ячеек памяти данного типа между двумя адресами.
Поскольку операции ++, --, * и & имеют одинаковый приоритет и выполняются справа налево, то нужно внимательно относиться к последовательности их выполнения при их сочетаниях, например, выражение
*p++
вычисляется как сначала *, затем ++ и значение указателя увеличивается на 1. Рассмотрим пример. Пусть имеются некоторые переменные и указатель, над которым последовательно выполняются операции.
int i1=10,i2=20,i3=30;
int *p=&i2;
i3 |
i2 |
i1 |
30 |
20 |
10 |
*&i2 -> 20
*&++i2 -> 21
30 |
21 |
10 |
*p -> 21
*p++ -> 21
*p -> 10
30 |
21 |
10 |
++*p -> 11
30 |
21 |
11 |
*--p -> 21
30 |
21 |
10 |
++*--p -> 31
31 |
21 |
10 |
++&i2; //Ошибка – не L-выражение
--&i2++; // То же самое
Любой адрес можно проверить на равенство операцией = = или на неравенство операцией != со специальной константой NULL, которая записывается вместо нуля (значение, эквивалентное нулю). Гарантируется, что никакой программный объект никогда не будет иметь адрес NULL. Указатель, имеющий значение NULL, не адресует никакую область памяти. Он называется нулевым указателем.