- •Структуры и алгоритмы обработки данных Введение
- •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. Файлы
3.4. Связь массивов с указателями
Любое действие, которое достигается индексированием массива, может быть выполнено также с помощью указателей, причём последний вариант будет быстрее.
Объявление
int a[5];
определяет массив из 5 элементов. Если объект *y объявлен как
int *y;
то оператор
y = &a[0];
присваивает переменной y адрес элемента a[0]. Если переменная y указывает на очередной элемент массива a, то y+1 указывает на следующий элемент, причём здесь выполняется масштабирование для приращения адреса с учётом длины объекта. Поскольку имя массива есть адрес его нулевого (начального) элемента (и указатель на этот элемент), то инструкцию y = &a[0] можно записать в виде
y = a;
Тогда элемент a[i] можно представить как *(a+i). Если у – указатель, следующие записи эквивалентны:
a[i] ~ *(a+i)
y[i] ~ *(y+i)
Таким образом, любой массив (и индексное выражение) можно представить при помощи указателей.
a[5] // массив
a – имя массива
y = &a[0] – адрес нулевого элемента массива
a – адрес нулевого элемента массива
x0 – содержимое 0-го элемента массива
a[0] = x0;
a[1] = x1;
x0 = *y = *a = a[0]
x1 = *(y+1) = *(a+1) = a[1]
В то же время между именем массива и соответствующим указателем есть одно существенное различие. Указатель – это переменная и y = a; или y++; – допустимые операции. Имя массива – это константа. Поэтому конструкции вида
a = y; a++;
использовать нельзя, т.к. значение константы постоянно и не может быть изменено.
Если указатели адресуют элементы одного массива, то их можно сравнивать (в операциях отношения). В то же время нельзя сравнивать, либо применять в арифметических операциях указатели на разные массивы. Указатели на элементы одного массива можно вычитать, результатом будет число элементов массива, расположенных между уменьшаемым и вычитаемым указателями.
Доступ к элементам многомерных массивов также возможен с помощью указателей:
b[i][j][k] ~ *(*(*(b+i)+j)+k)
Допускается комбинировать обе формы доступа к элементам многомерного массива:
b[i][j][k] ~ *(b[i][j]+k)
Обращение к элементу массива в языке C++ относится к постфиксному выражению вида
PE[IE]
Постфиксное выражение PE должно быть указателем на нужный тип, выражение IE должно быть одного из целых типов. Таким образом, если PE – указатель, адресующий массив, то PE[IE] – индексированный элемент этого массива. Выражение
*(PE+IE)
– другой способ доступа к этому же элементу массива. Возможна эквивалентная запись
*(IE+PE)
т.к. операция «+» – коммутативна, и следовательно
IE[PE]
адресует тот же элемент, что и PE[IE].
int m[] = {1,2,3,4,};
m[0] ~ 1
0[m] ~ 1
Иногда индексы могут иметь отрицательные значения. В этом случае указатель PE должен указывать не на начало массива.
Можно определить указатель, инициализированный именем массива, т.е. адресом начального элемента этого массива.
int m[] = {1,2,3,4,};
int *mp = m;
mp[0] ~ 1, *(mp+3) ~ 4
Иногда для такого указателя используется термин «указатель на массив». Следует внимательно относится к употреблению этого термина. Его применение для указателя, используемого в рассмотренном примере, является, в сущности, программистским жаргоном, не вполне оправданно и совсем не правильно с точки зрения типов данных. В дальнейшем будем для обозначения указателя, адресующего массив, применять термин «указатель на массив» в кавычках, для того, чтобы не путать его с типами данных указателей на массив.
«Указатель на массив», в отличие от имени массива, является обычным указателем, не константным, со всеми вытекающими последствиями. Операция sizeof, примененная к такому указателю, даёт количество байт, занятых именно указателем, а не адресуемым им массивом. Операция получения адреса & позволяет получить адрес указателя, а не адрес начала массива. Наконец, значение такого указателя может быть изменено, и он уже не будет адресовать массив.
Однако такой указатель, также как и имя массива, может использоваться для доступа к элементам массива с помощью операций [ ] и *, как в рассмотренном примере.