Указатели на многомерные массивы
Указатели на многомерные массивы в языке Си - это массивы массивов, т.е. такие массивы, элементами которых являются массивы. При объявлении таких массивов в памяти компьютера создается несколько различных объектов. Например, при выполнении объявления двумерного массива int arr2[4][3] в памяти выделяется участок для хранения значения переменной arr, которая является указателем на массив из четырех указателей. Для этого массива из четырех указателей тоже выделяется память. Каждый из этих четырех указателей содержит адрес массива из трех элементов типа int, и, следовательно, в памяти компьютера выделяется четыре участка для хранения четырех массивов чисел типа int, каждый из которых состоит из трех элементов. Распределение памяти для двумерного массива представлено ниже.
arr
b
arr[0] a arr[0][0] arr[0][1] arr[0][2]
arr[1] a arr[1][0] arr[1][1] arr[1][2]
arr[2] a arr[2][0] arr[2][1] arr[2][2]
arr[3] a arr[3][0] arr[3][1] arr[3][2]
Таким образом, объявление arr2[4][3] порождает в программе три разных объекта: указатель с идентификатором arr, безымянный массив из четырех указателей и безымянный массив из двенадцати чисел типа int. Для доступа к безымянным массивам используются адресные выражения с указателем arr. Доступ к элементам массива указателей осуществляется с указанием одного индексного выражения в форме arr2[2] или *(arr2+2). Для доступа к элементам двумерного массива чисел типа int должны быть использованы два индексных выражения в форме arr2[1][2] или эквивалентных ей *(*(arr2+1)+2) и (*(arr2+1))[2]. Следует учитывать, что с точки зрения синтаксиса языка Си указатель arr и указатели arr[0], arr[1], arr[2], arr[3] являются константами и их значения нельзя изменять во время выполнения программы.
Размещение трехмерного массива происходит аналогично и объявление float arr3[3][4][5] порождает в программе кроме самого трехмерного массива из шестидесяти чисел типа float массив из четырех указателей на тип float, массив из трех указателей на массив указателей на float, и указатель на массив массивов указателей на float.
При размещении элементов многомерных массивов они располагаются в памяти подряд по строкам, т.е. быстрее всего изменяется последний индекс, а медленнее - первый. Такой порядок дает возможность обращаться к любому элементу многомерного массива, используя адрес его начального элемента и только одно индексное выражение.
Например, обращение к элементу arr2[1][2] можно осуществить с помощью указателя ptr2, объявленного в форме int *ptr2=arr2[0] как обращение ptr2[1*4+2] (здесь 1 и 2 это индексы используемого элемента, а 4 это число элементов в строке) или как ptr2[6]. Заметим, что внешне похожее обращение arr2[6] выполнить невозможно, так как указателя с индексом 6 не существует.
Для обращения к элементу arr3[2][3][4] из трехмерного массива тоже можно использовать указатель, описанный как float *ptr3=arr3[0][0] с одним индексным выражением в форме ptr3[3*2+4*3+4] или ptr3[22].
Далее приведена функция, позволяющая найти минимальный элемент в трехмерном массиве. В функцию передается адрес начального элемента и размеры массива, возвращаемое значение - указатель на структуру, содержащую индексы минимального элемента.
struct index
{
int i,
int j,
int k
} min_index;
struct index * find_min (int *ptr1, int l, int m int n)
{
int min,i,j,k,ind;
min=*ptr1;
min_index.i = min_index.j = min_index.k=0;
for (i=0; i*(ptr1+ind)
{
min=*(ptr1+ind);
min_index.i=i;
min_index.j=j;
min_index.k=k;
}
return &min_index;
}
Операции с указателями
Над указателями можно выполнять унарные операции: инкремент и декремент. При выполнении операций ++ и -- значение указателя увеличивается или уменьшается на длину типа, на который ссылается используемый указатель.
int *ptr, a[10];
ptr = &a[5];
ptr++; // равно адресу элемента a[6]
ptr--; // равно адресу элемента a[5]
В бинарных операциях сложения и вычитания могут участвовать указатель и величина типа int. При этом результатом операции будет указатель на исходный тип, а его значение будет на указанное число элементов больше или меньше исходного.
int *ptr1, *ptr2, a[10];
int I = 2;
ptr1 = a+(i+4); // равно адресу элемента a[6]
ptr2 = ptr1-i; // равно адресу элемента a[4]
В операции вычитания могут участвовать два указателя на один и тот же тип. Результат такой операции имеет тип int и равен числу элементов исходного типа между уменьшаемым и вычитаемым, причем если первый адрес младше, то результат имеет отрицательное значение.
int *ptr1, *ptr2, a[10];
int i;
ptr1 = a+4;
ptr2 = a+9;
i = ptr1-ptr2; // равно 5
i = ptr2-ptr1; // равно -5
Значения двух указателей на одинаковые типы можно сравнивать в операциях ==, !=, <, <=, >, >= при этом значения указателей рассматриваются просто как целые числа, а результат сравнения равен 0 (ложь) или 1 (истина).
int *ptr1, *ptr2, a[10];
ptr1 = a+5;
ptr2 = a+7;
if (prt1 > ptr2) a[3] = 4;
В данном примере значение ptr1 меньше значения ptr2 и поэтому оператор a[3] = 4 не будет выполнен.