Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Основы алгоритмизации и программирования .Язык си.pdf
Скачиваний:
111
Добавлен:
16.03.2016
Размер:
4.49 Mб
Скачать
многомерного массива имеет следующий формат:
10.6. Многомерные массивы

10.5. Указатели на указатели

Указатели, как и переменные любого другого типа, могут объединяться в массивы.

Объявление массива указателей на целые числа имеет вид int *a[10], y;

Теперь каждому из элементов массива указателей a можно присвоить

адрес целочисленной переменной y, например: a[1]=&y;

В языке Си можно описать переменную типа «указательРна указатель». Это ячейка оперативной памяти (переменная), в которой будет

Чтобы теперь найти значение переменной y через данный элемент

массива а, необходимо записать *a[1].

храниться

адрес указателя на некоторую переменную. Признак такого типа

данных –

повторение символа «*» перед идентификаторомИпеременной.

Количество символов «*» определяет уровень вложенности указателей друг в

 

 

 

 

 

 

 

 

У

друга. При объявлении указателей на указатели возможна их одновременная

инициализация. Например:

 

 

 

Г

 

 

 

 

 

 

 

int a=5;

 

 

 

 

Б

 

int *p1=&a;

 

 

 

 

 

 

 

а

 

int **pp1=&p1;

 

 

 

 

int ***ppp1=&pp1;

 

 

Если присвоить переменной ка новое значение, например 10, то

одинаковые результаты будут получ ны в следующих операциях:

a=10;

*p1=10;

 

 

е

***ppp1=10;

 

 

**pp1=10;

 

Для доступа к

блас

 

ОП, отведенной под переменную а, можно

 

 

 

 

ти

 

 

использовать и индексы. Эквивалентны следующие выражения:

*p1

 

о

 

 

 

 

~

 

p1[0] ;

 

 

**pp1

и~

 

pp1[0][0] ;

 

 

***ppp1

~

 

ppp1[0][0][0] .

 

 

л

 

 

 

 

 

 

Факт чески, используя указатели на указатели, мы имеем дело с

многомернымибмассивами.

 

 

 

 

 

и

 

 

 

 

 

 

 

 

Б

 

 

 

 

 

 

 

 

Декларация

тип ID[размер1][размер2]…[размерN] =

{{список начальных значений},

{список начальных значений},

};

Списки начальных значений – атрибут необязательный.

77

Наиболее быстро изменяется последний индекс элементов массива, поскольку многомерные массивы в языке Си размещаются в памяти компьютера построчно друг за другом (см. следующую тему «Адресная функция»).

Рассмотрим особенности работы с многомерными массивами на конкретном примере двухмерного массива.

Например, пусть приведена следующая декларация двухмерного массива:

 

 

 

int m[3][4];

 

 

 

 

 

 

Идентификатор двухмерного массива – это указатель на массив

указателей (переменная типа указатель на указатель: int **m;).

 

Поэтому двухмерный массив m[3][4]; компилятор рассматривает как

 

 

 

 

 

 

 

 

 

 

 

Р

массив трех указателей, каждый из которых указывает на начало массива со

значениями размером по четыре элемента каждый.

В ОП данный массив

будет расположен следующим образом:

 

 

 

И

 

 

У

 

 

 

 

 

 

 

 

 

 

 

Указа-

m

 

 

 

З нач ения

Г

 

 

 

 

 

 

 

 

 

 

 

m [0]

m[0][0]

m[0][1]

m[0][2]

m[0][3]

 

тели

 

 

 

 

 

Б

 

 

 

m [1]

 

m[1][0]

m[1][1]

m[1][2]

m[1][3]

 

 

 

m [2]

 

m[2][0]

m[2][1]

m[2][2]

m[2][3]

 

 

 

 

 

 

 

а

 

 

 

 

 

 

(А)

 

 

(В)

 

 

 

Рис. 10.1. Схема размещениякэл ментов массива m размером 3×4

Причем в данном случае указаель m[1] будет иметь адрес m[0]+4*sizeof(int),

т.е. каждый первый элеме

 

следующей строки располагается за последним

элементом предыдущей стр ки.

 

 

 

Приведем

 

мер

нт

 

 

 

 

 

граммы конструирования массива массивов:

#include <stdio.h>

про

 

 

 

void main()

 

при

 

 

 

 

 

л

 

 

 

 

 

 

{

 

 

 

 

 

 

//

Декларация и инициализация

 

int x0[4] = { 1, 2, 3,4};

 

 

 

 

б

 

 

 

 

 

//

одномерных массивов

 

int x1[4] = {11,12,13,14};

 

int x2[4] = {21,22,23,24};

 

 

 

и

 

 

 

 

 

 

// Создание массива указателей

 

int *m[3] = {x0, x1, x2,};

 

int i,j;

 

 

 

 

 

 

 

 

Б

 

 

 

 

 

 

 

 

 

 

for (i=0; i<3; i++) {

 

 

 

 

 

printf("\n Cтрока %d) ", i+1); for (j=0; j<4; j++)

printf("%3d", m[ i ] [ j ]);

}

}

78

10.7. Адресная функция

Результаты работы программы:

Cтрока 1) 1 2 3 4

Cтрока 2) 11 12 13 14

Cтрока 3) 21 22 23 24

Такие же результаты будут получены и в следующей программе:

#include <stdio.h>

 

 

void main()

 

 

{

 

Р

int i, j;

 

int m[3][4] = { { 1, 2, 3, 4}, {11,12,13,14}, {21,22,23,24} };

for (i=0; i<3; i++) {

 

printf("\n %2d)", i+1);

 

for (j=0; j<4; j++)

И

printf(" %3d",m[ i ] [ j ]);

}

У

 

}

 

В последней программе массив

указателейГна соответствующие

массивы элементов создается компилятором Бавтоматически, т.е. данные массива располагаются в памяти последов тельно по строкам, что является основанием для декларации массива m в виде

 

 

 

 

 

а

 

 

int m[3][4] = {1, 2, 3, 4, 11, 12, 13, 14, 21, 22, 23, 24};

 

 

 

 

 

 

 

к

 

 

Замена скобочного выраж ния m[3][4] на m[12] здесь не допускается,

так как массив указателей не буд создан.

 

 

Таким образом, ис

 

е

в языке

Си

льзование многомерных массивов

связано с расходами памя и на создание массивов указателей.

 

 

Очевидна

схема

тразмещения такого массива в

памяти

последовательное

(друг за другом) размещение «строк» – одномерных

массивов со значе

 

по

 

 

 

ями (векторная организация памяти).

 

 

Обращениюник э ементам массива при помощи операции индексации

 

л

 

 

 

 

 

m[i][j] соответствует эквивалентное выражение, использующее адресную

арифмет ку –

*(*(m+i)+j).

 

 

 

 

Аналогбчным образом можно установить соответствие между

указател

массивами с произвольным числом измерений.

 

 

ями

 

 

 

 

 

 

 

Б

 

 

 

 

 

 

 

Векторная память поддерживается почти всеми языками высокого уровня и предназначена для хранения массивов различной размерности и различных размеров. Каждому массиву выделяется непрерывный участок памяти указанного размера. При этом элементы, например, двухмерного массива X размерностью nn2 размещаются в ОП в следующей последовательности:

79

Х(0,0), Х(0,1), Х(0,2),... Х(0, n2–1), ..., Х(1,0), Х(1,1), Х(1,2),... Х(1, n2–1), ..., Х(n1–1,0), Х(n1–1,1), Х(n1–1,2), ..., Х(n1–1, n2–1).

Адресация элементов массива определяется некоторой адресной функцией, связывающей адрес и индексы элемента.

Пример адресной функции для массива Х:

K(i, j) = n2*i + j;

где i = 0,1,2,... ,(n1–1); j = 0,1,2,... ,(n2–1); j – изменяется в первую очередь.

 

Адресная функция двухмерного массива A(n,m) будет выглядеть так:

 

 

 

 

 

 

N1 = K(i, j) = m*i + j,

 

 

 

 

Р

 

 

 

 

 

 

 

 

 

 

 

 

i=0,1,..., n–1; j=0,1,... , m–1 .

 

 

 

 

 

 

 

 

 

 

 

 

 

Тогда справедливо следующее:

 

 

 

 

 

У

 

 

 

 

 

 

 

 

A(i, j) ↔ B(K(i, j)) = B(N1),

 

 

 

 

 

 

 

 

 

 

Г

И

B – одномерный массив с размером N1 = n*m.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Б

 

 

 

 

 

Например, для двухмерного массива A(2,3) имеем:

 

 

 

 

(0,0)

(0,1)

 

(0,2)

(1,0)

 

(1,1)

(1,2)

 

– индексы массива А;

 

 

0

 

 

1

 

2

 

3

 

 

а

 

– индексы массива В.

 

 

 

 

 

 

 

 

4

5

 

 

 

Проведем расчеты:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

i = 0, j = 0 N1 = 3*0+0 = 0

 

B(0)

 

 

 

 

 

 

 

 

 

 

 

 

 

 

е

 

B(1)

 

 

 

 

 

 

 

 

i = 0, j = 1 N1 = 3*0+1 = 1

 

 

 

 

 

 

 

 

 

i = 0, j = 2

N1 = 3*0+2

к= 2 B(2)

 

 

 

 

 

 

 

 

 

 

 

 

 

т

 

 

 

B(3)

 

 

 

 

 

 

 

 

i = 1, j = 0 N1 = 3*1+0 = 3

 

 

 

 

 

 

 

 

 

i = 1, j = 1 N1 = 3*1+1 = 4

 

B(4)

 

 

 

 

 

 

 

 

 

 

о

 

 

 

 

B(5)

 

 

 

 

 

 

 

 

i = 1, j = 2 N1 = 3*1+2 = 5

 

 

 

 

 

 

 

 

 

 

 

и

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Аналогично получаем адресную функцию для трехмерного массива

Х(n1, n2, n3):

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

б

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

и

K(i, j, k) = n3*n2*i + n2*j + k ,

 

 

 

 

 

 

 

 

где i = 0,1,2,л... ,(n1–1); j = 0,1,2,... ,(n2–1); ); k = 0,1,2,... ,(n3–1); значение k

изменяется в первую очередь.

 

 

 

 

 

 

 

 

 

 

 

Б

размещения такого массива

потребуется участок ОП размером

 

Для

(n1*n2*n3)*sizeof(type). Рассматривая такую область как одномерный массив Y(0,1,..., n1*n2*n3), можно установить соответствие между элементом трехмерного массива X и элементом одномерного массива Y:

X(i, j, k) ↔ Y(K(i, j, k)) .

Необходимость введения адресных функций возникает лишь в случаях, когда требуется изменить способ отображения с учетом особенностей конкретной задачи.

80