В языке программирования С заложены средства для задания последовательностей упорядоченных данных [5.1]. Такие последовательности называются массивами. В массивах должны быть упорядочены данные одного и того же типа. В данной лабораторной работе будут рассматриваться массивы с целыми и вещественными типами данных, т.е. типы int, float или double.
Массивы данных могут быть одномерными (векторами размера 1 \[ \times \] n или n \[ \times \] 1), двухмерными (матрицами размера n \[ \times \] m) или многомерными (размера n \[ \times \] m \[ \times \] p...). В частности, для векторов и матриц в приведенной записи первый индекс означает количество строк, а второй (число или буква) – это количество столбцов. Для названия массива может быть использована переменная, состоящая из букв (буквы), букв с цифрами, букв с цифрами и знаком подчеркивания и т.д. в соответствии с правилами объявления переменных, принятых в языке С. Если размерность массива меньше, чем требуется, то компилятор не выдаст сообщения об ошибке. Выход за границы массивов должен следить только сам программист.
Одномерный массив – это список связанных однотипных переменных.
Общая форма записи одномерного массива [5.2]:
тип имя_массива[размер];
В приведенной записи элемент тип объявляет базовый тип массива. Количество элементов, которые будут храниться в массиве с именем имя_массива, определяется элементом размер.
В языке С индексация массива начинается с нуля. Например, если размер массива определен величиной 9, то в массиве можно хранить 10 элементов с индексацией 0, 1, 2, 3, 4, 5, 6, 7, 8, 9.
Доступ к отдельному элементу массива осуществляется с помощью индекса. Индекс описывает позицию элемента внутри массива.
Все массивы занимают смежные ячейки памяти, т.е. элементы массива в памяти расположены последовательно друг за другом. Ячейка памяти с наименьшим адресом относится к первому элементу массива, а с наибольшим – к последнему.
Для одномерных массивов общий размер массива в байтах вычисляется по формуле:
всего байт = размер типа в байтах * количество элементов
В языке С нельзя присвоить один массив другому. Для передачи элементов одного массива другому необходимо выполнить присвоение поэлементно.
Двухмерный массив представляет собой список одномерных массивов.
Общая форма записи двухмерного массива:
тип имя_массива[размер1] [размер2];
В приведенной записи размер1 означает количество строк двухмерного массива, а размер2 – количество столбцов.
В двухмерном массиве позиция любого элемента определяется двумя индексами. Индексы каждого из размеров массива начинаются с 0 (с нуля).
Место хранения для всех элементов массива определяется во время компиляции. Память, выделенная для хранения массива, используется в течение всего времени существования массива.
Для двухмерных массивов общий размер массива в байтах вычисляется по формуле:
всего байт = число строк * число столбцов * размер типа в байтах
Общая форма записи многомерного массива:
тип имя_массива[размер1] [размер2]... [размерN];
Индексация каждого размера начинается с нуля. Элементы многомерного массива располагаются в памяти в порядке возрастания самого правого индекса. Поэтому правый индекс будет изменяться быстрее, чем левый (левые).
При обращении к многомерным массивам компьютер много времени затрачивает на вычисление адреса, так как при этом приходится учитывать значение каждого индекса [5.2]. Следовательно, доступ к элементам многомерного массива происходит значительно медленнее, чем к элементам одномерного. В этой связи использование многомерных массивов встречается значительно реже, чем одномерных или двухмерных массивов.
Для многомерных массивов общий размер многомерного массива в байтах вычисляется по формуле:
всего байт = размер1* размер2*...* размерN *размер типа в байтах
Очевидно, многомерные массивы способны занять большой объем памяти, а программа, которая их использует, может очень быстро столкнуться с проблемой нехватки памяти.
Для определения размера типа в байтах применяется функция sizeof(), которая возвращает целое число. Например, sizeof(float).
В языке С массивы при объявлении можно инициализировать [5.2].
Общая форма инициализации массива:
тип имя_массива[размер1] * [размерN] = {список_значений};
В список_значений входят константы, разделенных запятыми. Типы констант должны быть совместимыми с типом массива.
Пример инициализации одномерного массива:
int A[5] = {1, 2, 3, 4, 5};
При этом A[0] = 1, A[1] = 2 и т.д.
При инициализации многомерного массива для улучшения наглядности элементы инициализации каждого измерения можно заключать в фигурные скобки.
Пример инициализации двухмерного массива:
int MN[3][4] = { {1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12} };
Массив MN[3][4] – это матрица, у которой 3 строки и 4 столбца.
Для многомерных массивов инициализацию можно также проводить с указанием номера инициализируемого элемента.
Пример инициализации трехмерного массива:
int XYZ[2][3][4] = { { {1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12} }, { {13, 14, 15, 16}, {17, 18, 19, 20}, {21, 22, 23, 24} } };
Как видно, массив XYZ содержит два блока, каждый из которых есть матрица размера 3 \[ \times \] 4, т.е. 3 строки и 4 столбца.
В языке С возможна инициализация безразмерных массивов. Например, для одномерного массива:
int A[ ] = {1, 2, 3, 4, 5};
В многомерном массиве размер самого левого измерения также можно не указывать. В частности, для инициализации массива MN[3][4] допустима следующая запись:
int MN[][4] = { {1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12} };
При инициализации многомерных массивов необходимо указать все данные (размерности) за исключением крайней слева размерности. Это нужно для того, чтобы компилятор смог определить длину подмассивов, составляющих массив, и смог выделить необходимую память. Рассмотрим пример безразмерной инициализации для трехмерного массива целых чисел:
int XYZ[][3][4] = { { {1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12} }, { {13, 14, 15, 16}, {17, 18, 19, 20}, {21, 22, 23, 24} } };
Вывод трехмерного массива на консоль (дисплей) можно выполнить по следующей программе:
#include <stdio.h> #include <conio.h> int main (void) { int i, j, k; int XYZ[][3][4] = { { {1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12} }, // 1-й { {13, 14, 15, 16}, {17, 18, 19, 20}, {21, 22, 23, 24} } }; // 2-й for (i = 0; i < 2; ++i) { printf("\n"); for (j = 0; j < 3; ++j) { printf("\n"); for (k = 0; k < 4; ++k) printf(" %3d", XYZ[i][j][k]); } } printf("\n\n Press any key: "); _getch(); return 0; }
Пример 1. Напишите программу заполнения одномерного массива случайными числами из интервала от 1 до 15 по случайному равномерному закону. Отсортировать массив случайных чисел по возрастанию.
Для решения поставленной задачи применим сортировку методом прямого выбора [5.3]. Алгоритм сортировки заключается в следующем:
Программный код решения примера:
#include <stdio.h> #include <conio.h> #include <time.h> #include <stdlib.h> #define Left 1 #define Right 15 #define N 10 int main (void) { float R, r, min; float A[N]; int i, j, k; unsigned int some; long int L; L = (long) time(NULL); // Системное время some = (unsigned) L; // Приведение типов srand(some); // Задание исходного случайного числа для rand() printf("\n\t The initial array of random numbers in the interval [%d, %2d]\n", Left, Right); for (i = 0; i < N; ++i) {// Случайное число из интервала [0,1] r = (float) rand()/RAND_MAX; // Формирование случайного числа из заданного интервала R = Left + (Right - Left) * r; // Заполнение массива случайными числами A[i] = R; } // Печать элементов исходного массива for (i = 0; i < N; ++i) printf("\n\t %5d) %10.4f", i + 1, A[i]); // Сортировка методом выбора for (i = 0; i < (N - 1); ++i) { min = A[i]; k = i; for (j = i + 1; j < N; ++j) if (A[j] < min) { k = j; min = A[k]; } A[k] = A[i]; A[i] = min; } // Печать отсортированного массива по возрастанию printf("\n\n\t Sort an array:\n"); for (i = 0; i < N; ++i) printf("\n\t %5d) %10.4f", i + 1, A[i]); printf("\n\n Press any key: "); _getch(); return 0; }
Возможный результат выполнения программы показан на рис. 5.1.
В программе использованы директивы препроцессора для задания левой границы ( #define Left 1 ), правой границы ( #define Right 15 ) и размера одномерного массива ( #define N 10 ). Включены дополнительные библиотеки time.h – для обращения к функциям системного времени, stdlib.h – для обращения к функциям генерации псевдослучайных чисел.