p align="left">Таким образом, объявление 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] из трехмерного массива тоже можнo использовать указатель, описанный как 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 не будет выполнен. Массивы указателей В языке СИ элементы массивов могут иметь любой тип, и, в частности, могут быть указателями на любой тип. Рассмотрим несколько примеров с использованием указателей. Следующие объявления переменных int a[]={10,11,12,13,14,}; int *p[]={a, a+1, a+2, a+2, a+3, a+4}; int **pp=p; порождают программные объекты, представленные на схеме pp pа . . . . . в в в в в 18 aа 11 12 13 14 15 Схема размещения переменных при объявлении. При выполнении операции pp-p получим нулевое значение, так как ссылки pp и p равны и указывают на начальный элемент массива указателей, связанного с указателем p ( на элемент p[0]). После выполнения операции pp+=2 схема изменится и примет вид, изображенный pp pа . . . . в в в в в aа 10 11 12 13 14 Схема размещения переменных после выполнения операции pp+=2. Результатом выполнения вычитания pp-p будет 2, так как значение pp есть адрес третьего элемента массива p. Ссылка *pp-a тоже дает значение 2, так как обращение *pp есть адрес третьего элемента массива a, а обращение a есть адрес начального элемента массива a. При обращении с помощью ссылки **pp получим 12 - это значение третьего элемента массива a. Ссылка *pp++ даст значение четвертого элемента массива p т.е. адрес четвертого элемента массива. Если считать, что pp=p, то обращение *++pp это значение первого элемента массива a (т.е. значение 11), операция ++*pp изменит содержимое указателя p[0], таким образом, что он станет равным значению адреса элемента a[1]. Сложные обращения раскрываются изнутри. Например обращение *(++(*pp)) можно разбить на следующие действия: *pp дает значение начального элемента массива p[0], далее это значение инкременируется ++(*p) в результате чего указатель p[0] станет равен значению адреса элемента a[1], и последнее действие это выборка значения по полученному адресу, т.е. значение 11. В предыдущих примерах был использован одномерный массив, рассмотрим теперь пример с многомерным массивом и указателями. Следующие объявления переменных порождают в программе объекты представленные на схеме int a[3][3]={ { 11,12,13 }, { 21,22,23 }, { 31,32,33 } }; int *pa[3]={ a,a[1],a[2] }; int *p=a[0]; Схема размещения указателей на двумерный массив. Согласно этой схеме доступ к элементу a[0][0] получить по указателям a, p, pa при помощи следующих ссылок: a[0][0], *a, **a[0], *p, **pa, *p[0]. Рассмотрим теперь пример с использованием строк символов. Объявления переменных можно изобразить схемой представленной: char *c[]={ "abs", "d", "yes", "no" }; char **cp[]={ c+3, c+2 , c+1 , c }; char ***cpp=cp; Схема размещения указателей на строки. Динамическое размещение массивов При динамическом распределении памяти для массивов следует описать соответствующий указатель и присваивать ему значение при помощи функции calloc. Одномерный массив a[10] из элементов типа float можно создать следующим образом float *a; a=(float*)(calloc(10,sizeof(float)); Для создания двумерного массива вначале нужно распределить память для массива указателей на одномерные массивы, а затем распределять память для одномерных массивов. Пусть, например, требуется создать массив a[n][m], это можно сделать при помощи следующего фрагмента программы: #include main () { double **a; int n,m,i; scanf("%d %d",&n,&m); a=(double **)calloc(m,sizeof(double *)); for (i=0; i<=m; i++) a[i]=(double *)calloc(n,sizeof(double)); . . . . . . . . . . . . } Аналогичным образом можно распределить память и для трехмерного массива размером n,m,l. Следует только помнить, что ненужную для дальнейшего выполнения программы память следует освобождать при помощи функции free. #include main () { long ***a; int n,m,l,i,j; scanf("%d %d %d",&n,&m,&l); /* -------- распределение памяти -------- */ a=(long ***)calloc(m,sizeof(long **)); for (i=0; i<=m; i++) { a[i]=(long **)calloc(n,sizeof(long *)); for (j=0; i<=l; j++) a[i][j]=(long *)calloc(l,sizeof(long)); } . . . . . . . . . . . . /* --------- освобождение памяти ----------*/ for (i=0; i<=m; i++) { for (j=0; j<=l; j++) free (a[i][j]); free (a[i]); } free (a); } Рассмотрим еще один интересный пример, в котором память для массивов распределяется в вызываемой функции, а используется в вызывающей. В таком случае в вызываемую функцию требуется передавать указатели, которым будут присвоены адреса выделяемой для массивов памяти. Пример: #include main() { int vvod(double ***, long **); double **a; /* указатель для массива a[n][m] */ long *b; /* указатель для массива b[n] */ vvod (&a,&b); .. /* в функцию vvod передаются адреса указателей, */ .. /* а не их значения */ .. } int vvod(double ***a, long **b) { int n,m,i,j; scanf (" %d %d ",&n,&m); *a=(double **)calloc(n,sizeof(double *)); *b=(long *)calloc(n,sizeof(long)); for (i=0; i<=n; i++)
Страницы: 1, 2, 3, 4, 5
|