Курсовая работа: Массивы. Двумерные массивы
Размещение трехмерного
массива происходит аналогично и объявление 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 передаются адреса указателей, */
Страницы: 1, 2, 3, 4, 5, 6, 7, 8 |