Массивы в С С

Лабораторная работа



На тему: МАССИВЫ в С/С++


Поэлементные операции



1. Одномерные (линейные) массивы



Линейным массивом в программе на C++ называется упорядоченный набор однотипных переменных, которые располагаются в памяти последовательно 





5

21

0

12

8

-3

14

-9

25

…

элемент 0

элемент 1

элемент 2

элемент n-1

A[0]

A[1]

A[2]

A[3]

A[4]

A[5]

A[6]

A[n-2]

A[n-1]



Массив является простейшей структурой данных, облегчающей работу с большими объемами информации путем их упорядочения. В случае с массивами, упорядочение происходит за счет индексирования элементов, то есть обращения к каждому из них по порядковому номеру. Показанный на рисунке массив состоит из n элементов с индексами от 0 до n-1, в который записаны числа 5, 21, 0, 12 и т.д. 

Любой массив в C++ характеризуется тремя параметрами: именем, типом элементов и размером. Как и обычная переменная, перед использованием массив должен быть объявлен. Общая форма записи объявления: 



тип_элементов имя_массива[размер_массива];



Здесь тип_элементов – это любой из известных стандартных типов (int, float, double, char и т.д.), имя_массива – уникальное имя (идентификатор), используемое для обращения к массиву, размер_массива – количество его элементов. В качестве последнего параметра в объявлении может быть использована только целочисленная константа или константное выражение. Примеры объявлений



	int A[15];	// массив из 15 целочисленных элементов с именем A

	float x[3];	// массив x из 3-х элементов типа float



Объявление массива является командой компилятору на выделение памяти для хранения его элементов. Общее количество выделенной памяти зависит не только от числа элементов, но и от размера каждого элемента, то есть от его типа. Например, текстовая строка из 1000 символов (тип char) займет P = 1000*sizeof(char) = 1000 байтов, а массив из такого же количества вещественных чисел двойной точности (тип double) займет уже в восемь раз больше – P = 1000*sizeof(double) = 8000 байтов. 

Нумерация элементов в массиве начинается с нуля. Таким образом, первый элемент массива имеет индекс 0, а последний – индекс n-1, где n – размер массива. Обращение к элементу производится с использованием имени массива и индекса элемента в квадратных скобках. Например, запись “x[0] = 5.5;” означает “присвоить значение 5.5 нулевому элементу массива x”.

Для работы с массивами характерным является использование итерационных циклов for. С их помощью организуется выполнение однотипных операций со всеми элементами массива, в частности, поэлементный ввод-вывод, поэлементные арифметические операции и др. Рассмотрим это на следующем примере.

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

Решение. Будем использовать массив с именем A и размером 10 для хранения введенных чисел. Ввод данных и суммирование организуем поэлементно с помощью циклов for. 

	
//----------- Сумма элементов массива -------------

	 

	#pragma hdrstop

	#include <conio.h>

	#include <iostream.h>

	 

	#pragma argsused

	int main(int argc, char* argv[])

	{

	  int A[10];	// объявляем массив из 10 целых

	  for(int i=0; i<10; i++)	// организуем цикл по i от 0 до 9

	  {

	   cout << "input A[" << i << "] = "; 	// приглашение

	   cin >> A[i];	// вводим A[i]

	  }

	  int sum = 0;	// объявляем переменную

	  for(int i=0; i<10; i++)	// организуем цикл

	   sum = sum + A[i];	// в цикле суммируем элементы

	

	  cout << "\nSumma: " << sum;	// выводим результат на экран

	  getch();	// задержка

	  return 0;

	}

	//-------------------------------------------------



Наберите код этой программы в среде Turbo C++ и запустите ее на выполнение. Проверьте правильность получаемых с ее помощью результатов. Нарисуйте блок-схему и объясните алгоритм ее работы. Объясните смысл использования дополнительной переменной sum в этой программе.


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



Массивы в программах C++ могут быть не только линейными. Довольно частым является использование двух - (и более) -мерных структур. К примеру, прямоугольная матрица – типичная структура, представимая с помощью двумерного массива; а 3D-тензор может быть записан как соответствующий трехмерный массив.

Многомерный массив в C++ организован по принципу «массива массивов». Общий формат его объявления



тип имя[N1][N2]…[NM];





5

21

0

12

8

-3

14

G[0][0]

23

55

-1

0

78

44

4

0

9

33

0

-2

0

G[0][3]

G[1][0]

G[4][0]

G[4][3]



Здесь M – число индексов (или размерность) массива. Индексы изменяются в пределах от 0 до N1 – 1, от 0 до N2 – 1, от 0 до N3 – 1, ..., от 0 до NM – 1, соответственно. 

К примеру, запись int G[5][4]; означает объявление двумерного массива целых чисел с именем G и размерами 54. На рис. справа такой массив представлен в виде таблицы. Здесь первый индекс является номером строки, второй – номером столбца таблицы, с диапазонами изменения от 0 до 4, и от 0 до 3, соответственно.

Доступ к элементам многомерного массива в программе производится так же, как и в одномерном случае, то есть путем указания имени массива и набора индексов в квадратных скобках. Например, операция присваивания значения 0 последнему элементу будет записана как G[4][3] = 0.

При работе с многомерными массивами удобно использовать вложенные циклы for. С их помощью можно выполнить заданное действие с каждым из элементов массива путем перебора всех значений индексов. Приведенный ниже фрагмент программного кода выводит на экран все элементы массива G.

	

	for(int i=0; i<5; i++)	// цикл по строкам i

	{ 

	 for(int j=0; j<4; j++)	// цикл по строкам j

	  cout << G[i][j] << “\t”;	// выводим G[i][j]

	 cout << endl;	// перевод на новую строку

	}



Алгоритм работы этого фрагмента иллюстрируется следующей блок-схемой





i = 0

j = 0

G[i][j]

j++

(j<4)?

(+)

(–)

(i<5)?

(+)

(–)

i++

endl

Здесь внешний цикл по I последовательно пробегает все значения от 0 до 5. При каждом i запускается внутренний цикл по j от 0 до 4. В теле этого цикла на экран выводятся значение элемента G[i][j] и знак табуляции (горизонтальный отступ). Внутренний цикл продолжается до тех пор, пока не будут перебраны все значения j, то есть не будет выведена вся i-я строка. По завершении внутреннего цикла, экранный курсор перемещается на новую строку манипулятором endl, и далее внешний цикл продолжает свою работу, последовательно выводя на экран другие строки массива. 

Пример 2. Имеется вещественная квадратная матрица размером 44. Напишите программу, вычисляющую произведение элементов в каждой ее строке. Матрица вводится с клавиатуры.

Решение. Условимся использовать для хранения матрицы массив float X[4][4]. Расчет произведения реализуем согласно следующему алгоритму. Введем вспомогательную переменную (к примеру, float P). Заметим, что в i-й строке расположены элементы X[i][j], где j меняется от 0 до 3, включительно. Организуем цикл по j, внутри которого будем домножать P на X[i][j]. Тогда на выходе из цикла P будет содержать искомое произведение. Выведем его на экран и продолжим расчеты для следующей строки. Для правильной работы алгоритма перед началом расчетов в каждой строке матрицы необходимо присвоить P значение 1. 



//-------- Произведение элементов строки ----------



#pragma hdrstop

#include <conio.h>

#include <iostream.h>

 

#pragma argsused

int main(int argc, char* argv[])

{

	 float X[4][4];	// объявляем массив 44 

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

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

	  {

	   cout << "input X[" << i <<

	       "," << j << "] = ";

	   cin >> X[i][j];	// вводим элементы матрицы

	  }

	

	 cout << "\n Results:\n";

	 for(int i=0; i<4; i++)	// цикл по строкам

	 {

	  float P = 1.0;	// вспомогательная переменная

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

	   P = P * X[i][j];	//  домножаем P на X[i][j]

	  cout << "\n proizvedenie " << i 

	     << "-i stroki = " << P;	// выводим результат на экран

	 }

 getch();

 return 0;

}

//-------------------------------------------------



Наберите и откомпилируйте код этой программы в Turbo C++. Проверьте результаты, выдаваемые программой.



3. Типизированные константы



В рассмотренном выше примере текст программы содержит явные выражения, задающие размеры массива X. Он объявлен как float X[4][4], то есть с явным указанием размерности, и далее везде по ходу программы цифра 4 используется явно при записи циклов for.

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

Этого можно избежать, если ввести типизированные константы, которые будут использоваться для указания размеров массива. Так же, как и переменная, типизированная константа имеет уникальное имя и тип, однако ее значение не может быть изменено по ходу выполнения программы. Это дает право использовать ее в объявлении массива. 

Типизированная константа должна быть объявлена с ключевым словом const, вслед за которым указывается ее тип (int, float, double, char, и т.д.), далее, через пробел, – ее имя и инициализирующее выражение

const тип имя = выражение;

Например, запись const int N = 4; означает объявление целочисленной константы с именем N и значением 4. В рассмотренном выше примере N могла быть использована как при объявлении массива float X[N][N], так и везде далее вместо 4. Такая программа будет легко масштабируемой в силу того, что в ней достаточно изменить значение N работы с массивами других размеров.

Типизированная константа может быть объявлена в любом месте программы до момента своего первого использования. Однако хорошим стилем считается объявление всех констант в самом начале программного файла, после подключения библиотек директивами #include.


4. Инициализация элементов массива



В рассмотренных выше примерах начальные значения элементов массива задавались пользователем с клавиатуры. Можно поступить иначе и каждому из элементов присвоить начальное значение с помощью оператора «=». Это потребует довольно большого количества записей – по отдельному оператору для каждого из элементов. C++ дает программисту еще один, более удобный, способ. Массив может быть инициализирован при объявлении. Для этого в строке объявления сразу вслед за указанием типа элементов, имени массива и его размеров, записывается знак присваивания, и далее в фигурных скобках задаются значения элементов через запятую

тип имя[размерN] = { знач1, знач2, знач3, ..., значN };

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

В следующем примере

	float mark[5] = { 7.3, 4.0, 2.2, 12.1, 8.9 };	

создается массив с именем mark, состоящий из 5 чисел типа float, которые инициализируются значениями 7.3, 4.0, 2.2 и т.д. Это эквивалентно следующему набору операторов 

float mark[5]; mark[0]=7.3; mark[1]=4.0; mark[2]=2.2 и т.д.

Многомерный массив также может быть инициализирован в строке объявления. Для этого достаточно помнить о том, что многомерный массив в C++ – это «массив массивов», о чем уже говорилось выше. Пример

	int F[3][3] = { {3, 0, 2} , (1, 9, 8}, {5, 7, 4} };

Здесь создается двумерный массив с именем F и размерами 33, элементы которого получают начальные значения F[0][0] = 3, F[0][1] = 0, F[0][2] = 2, F[1][0] = 1 и т.д. 

Пример 3. Пусть и  – две квадратные матрицы размером 33. Требуется написать программу, рассчитывающую их произведение 

Решение. Искомое произведение  – это также матрица 33, элементы которой рассчитываются по формуле  (). Аналогично Примеру 1, организуем суммирование с помощью вспомогательной переменной s и цикла по k. Для перебора всех  используем два дополнительных вложенных друг в друга цикла по i и по j. Эти циклы будут внешними по отношению к циклу по k. 



//-------------- Перемножение матриц --------------

 

#pragma hdrstop

#include <conio.h>

#include <iostream.h>

 

#pragma argsused

	const int N = 3;	// используем константу N=3

	

	int main(int argc, char* argv[])

	{

	  float A[N][N] = { {1, 1, 1},

	           {2, 2, 2},

	           {3, 3, 3} };	// исходная матрица A

	  float B[N][N] = { {1, 2, 3},

	           {1, 2, 3},

	           {1, 2, 3} };	// исходная матрица B

	  float C[N][N];	// матрица произведения С

	

	  for(int i=0; i<N; i++)	// цикл по строкам С

	  {

	   for(int j=0; j<N; j++)	// цикл по столбцам С

	   {

	    float s = 0.0;	// вспомогательная переменная

	    for(int k=0; k<N; k++)	// цикл суммирования по k

	     s += A[i][k]*B[k][j];	// добавляем к s новое слаг-ое

	    C[i][j] = s;	// записываем s в C[i][j]

	   }

	  }

	

	  cout << " Results: \n";	// далее выводим C на экран

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

	  {

	   for(int j=0; j<N; j++)

	    cout << C[i][j] << "\t";

	   cout << endl;

	  }

	  getch();

	  return 0;

}

//-------------------------------------------------