Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Програм. и основы алгорит.з.о..doc
Скачиваний:
5
Добавлен:
17.11.2019
Размер:
293.89 Кб
Скачать

2.3.2 Массивы

Если требуется работать с группой величин одного типа, их располагают в памяти последовательно и дают им общее имя, а различают по порядковому номеру. Такая последовательность однотипных величин называется массивом (вектором).

Различают массивы:

  • одномерные (имеют одну размерность) – вектора;

  • многомерные (две и более размерностей).

Из многомерных массивов наиболее часто работают с двумерными массивами (матрицами, таблицами).

Порядок работы с массивами следующий:

1) Объявление массивов (или их определение), при котором указывается тип элементов массива (а он у всех одинаков), имя массива и количество элементов или количество строк и столбцов для матрицы. При описании массивов каждая размерность задается отдельно в квадратных скобках. У матриц (двумерных массивов) первая размерность означает количество строк, вторая – количество столбцов. Например:

float a [10]; // описание вектора из 10 вещественных чисел

int B [3][4]; /* описание матрицы из 12 (3х4)целых элементов,

имеющей 3 строки (с номерами 0 – 2) и 4 столбца (с номерами 0-3) */

При объявлении массивов а[10] и В[3][4] определяется не только объем выделяемой памяти для 10 элементов массива a (с №№ 0 – 9) и 12 элементов массива B (3 строки и 4 столбца), но и для указателей-констант с именами a и В, значение которых равно адресу первых по счету (нулевых) элементов массивов.

Под массивы системой выделяется непрерывный участок памяти (его объем определяется системой при объявлении массива), где элементы массивов хранятся последовательно, элементы матрицы располагаются в последовательных ячейках памяти построчно.

Элементы массива нумеруются с нуля. Так же, с нуля, начинается нумерация строк и столбцов матрицы.

2) Инициализация массивов (заполнение массивов значениями) может производиться различными способами. Наиболее распространенные из них:

  • явная инициализация (значения записываются в фигурных скобках при описании массива);

  • с помощью генератора случайных чисел (см. функции rand(), random(), randomize());

  • ввод значений с клавиатуры или интерактивный ввод (используются операторы cin>> и scanf()).

Явная инициализация вектора производится путем задания набора начальных значений эле­ментов, разделенных запятыми и заключенных в фигурные скобки, по следующей форме:

int b[2] ={1,2}; // b[0]=1, b[1]=2

Аналогично одномерным векторам многомерные массивы могут быть так же инициализированы при их описании, например:

float BV[3][3][3]={1.1,1.2,1.3,

2.1,2.2,2.3,

3.1,3.2,3.3}

В многомерном массиве можно не указывать первую левую размерность, в этом случае элементы строк заключаются в фигурные скобки, например:

int V[ ][3]={{1,2},{4,5,6},{7}}; // элементы первой строки

// получат значения 1,2 и 0, //второй - 4,5,6, а третей - 7,0,0, //т.к. при недостатке значений //система дописывает //необходимое число нулей

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

for (int i=0; i<n;i++){

cout<<"введите a[ "<<i<<" ] эл-т"<<'\n';

cin>>a[i]; }

Рассмотрим на примере формирование матрицы с помощью генератора случайных чисел:

. . .

int x[5][4]; // описание массива х с 20 элементами (5х4)

. . .

randomize();

. . .

for (int i=0; i<5; i++) // перебор строк от нулевой по пятую

for (int j=0; j<4; j++) // перебор элементов каждой строки

{ x[i][j] = random(10) – 5;

. . .

}

  1. Обработка массива.

Обработка массива осуществляется по разработанному для задачи алгоритму, обращаясь к каждому конкретному элементу массива.

В C++ разрешены два способа доступа к элементам массива:

  • классический - с помощью индексов;

  • с использованием механизма указателей.

При классическом способе после имени указывается:

  • для вектора – номер его элемента (нумерация начинается с нуля);

  • для массива – номер строки и номер столбца, в котором находится элемент (нумерация строк и столбцов также начинается с нуля).

Например:

V[1][2]=3; /* использование индексов, элемент находится в столбце c номером 2 строки с номером 1 */

f = a[14]; // переменной f присваивается значение элемента

// вектора с номером 14 (по счету он 15-тый)

Обращение через указатели-константы и указатели-переменные отличается. Указатель-константа, как нам известно, не меняет своего значения, поэтому для доступа к элементам массива используются величины, называемые смещениями. Если необходимый нам элемент расположен выше или левее элемента, на который указывает указатель-константа, то смещение имеет знак “-“ (“минус”), если ниже или правее – то знак “+” (“плюс”). Если положение указателя-константы совпадает с началом массива, то значения смещений совпадают с номерами строки и столбца, в которых находится необходимый нам элемент. Это очень удобно при произвольном обращении к элементам массива. Порядок указания смещений относительно указателя совпадает с порядком соответствующих им размерностей, так для матрицы первое смещение соответствует смещению по строкам, а второе – по столбцам. Каждый раз после смещения в нужном направлении необходимо разыменовать полученную величину. Следовательно, разыменований будет столько, сколь раз мы смещались относительно указателя.

*(*(V+1)+2)=3; // *(V[1]+2)=3; или V[1][2]=3.

В данном случае сначала следует обратиться к первой строке массива, т.е. к одномерному массиву V[1]. Для этого надо прибавить к адресу начала массива (V) смещение, равное номеру строки, и выполнить разыменование: (V+1). При сложении указателя с константой учитывается длина адресуемого элемента, т.е. 1 * (5 * sizeof(int)), поскольку элементом является строка, состоящая из 5 элементов типа int.

Далее требуется обратиться ко второму элементу полученного массива. Для получения его адреса опять применяется сложение указателя с константой (т.е. 2 * sizeof(int)), а затем применяется операция разыменования для получения значения элемента: *(*(V+1)+2).

*(V[1]+2)=3; // или V[1][2]=3,где V[1] - указатель на 1-ю

// строку, 2 - смещение

*(arr1+5)+=3; // или arr1[5]+=3, где arr1 – указатель на вектор,

// 5 – смещение

Применение меха­низма указате­лей основано на использовании факта, что имя вектора является указателем - константой, рав­ной адресу начала вектора - первого байта первого элемента вектора (arr1==&arr1[0]). В результате этого, используя операцию разыменования “*” можно обес­печить доступ к лю­бому элементу вектора. Так, эквивалентны будут обращения к i-му эле­менту вектора с исполь­зованием индекса - arr1[i] и ссылки *(arr1+i), поскольку (arr1+i)==&arr1[i].

*(*(V+1)+2)=3; /* V - указатель-const на начало массива с

именем «V» */

Обращение посредством указателя-переменной производится иначе. Чтобы обратиться к другому элементу массива значение указателя должно быть изменено, что соответствует поэлементному “перемещению” указателя по массиву, например:

int K[3][5], *p=K; // объявили матрицу К и поставили

// указатель р на начало матрицы

. . .

p+= 7; // «передвинули» указатель на 7 элементов вправо,

// т.е. 1 строку вниз + 2 столбца вправо

*p=9; // присвоили К[1][2]значение «9»

p=p-3; //“передвинули» указатель на 3 элемента влево (1

// строку вверх + 2 элемента вправо)

Если до начала работы программы неизвестно сколько в массиве элементов, то следует использовать динамические массивы. Память под них выделяется с помощью

  • операции new;

  • функции malloc (устаревший способ).

Адрес начала такого массива хранится в переменной, называемой указателем. Например:

int n=10; // количество элементов в векторе, м.б. и

// переменной

int *a = new int[n]; /* описание указателя на целую величину,

которому присваивается адрес начала непрерывной области динамической памяти, необходимой для хранения n величин типа int*/

double *b = (double *)malloc(n * sizeof (double));

/* для выделения памяти под n элементов типа double с использованием функции malloc */

. . .

int n;

const int m=5;

cin>>n:

int **b = (int **) new int [n][m]; /* b – указатель на указатель, поэтому перед присвоением требуется выполнить преобразование типа ((int **)) */

int (*a)[m] = new int [n][m]; /* a – указатель на массив из m элементов типа int */

. . .

int n_str, n_stl;

cout<<”введите кол-во строк и столбцов:” ;

cin>>n_str>>n_stl;

int **d = new int *[n_str];

for (int i=0; i<n_str; i++)

d[i] = new int [n_stl]; // самый универсальный способ

Обращение к элементам динамических массивов производится так же, как и к элементам обычных массивов.

Если динамический массив в какой-то момент работы программы перестает быть нужным и мы собираемся впоследствии использовать эту память повторно, необходимо освободить ее с помощью операции delete[], например:

delete [] a; // размерность массива не указывается!