При решении задач с большим количеством данных одинакового типа использование переменных с различными именами, не упорядоченных по адресам памяти, затрудняет программирование. В подобных случаях в языке Си используют объекты, называемые массивами.
Массив — это непрерывный участок памяти, содержащий последовательность объектов одинакового типа, обозначаемый одним именем.
Массив характеризуется следующими основными понятиями:
Элемент массива (значение элемента массива) – значение, хранящееся в определенной ячейке памяти, расположенной в пределах массива, а также адрес этой ячейки памяти.
Каждый элемент массива характеризуется тремя величинами:
- адресом элемента — адресом начальной ячейки памяти, в которой расположен этот элемент;
- индексом элемента (порядковым номером элемента в массиве);
- значением элемента.
Представленный на рисунке массив содержит q элементов с индексами от 0 до q-1. Каждый элемент занимает в памяти компьютера k байт, причем расположение элементов в памяти последовательное.
Адреса i-го элемента массива имеет значение
n+k·i
Адрес массива представляет собой адрес начального (нулевого) элемента массива. Для обращения к элементам массива используется порядковый номер (индекс) элемента, начальное значение которого равно 0. Так, если массив содержит q элементов, то индексы элементов массива меняются в пределах от 0 до q-1.
Длина массива – количество байт, отводимое в памяти для хранения всех элементов массива.
ДлинаМассива = РазмерЭлемента * КоличествоЭлементов
Для определения размера элемента массива может использоваться функция
int sizeof(тип);
Например,
sizeof(char) = 1;
sizeof(int) = 4;
sizeof(float) = 4;
sizeof(double) = 8;
Объявление и инициализация массивов
Для объявления массива в языке Си используется следующий синтаксис:
тип имя[размерность]={инициализация};
Инициализация представляет собой набор начальных значений элементов массива, указанных в фигурных скобках, и разделенных запятыми.
int a[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; // массив a из 10 целых чисел
Если количество инициализирующих значений, указанных в фигурных скобках, меньше, чем количество элементов массива, указанное в квадратных скобках, то все оставшиеся элементы в массиве (для которых не хватило инициализирующих значений) будут равны нулю. Это свойство удобно использовать для задания нулевых значений всем элементам массива.
int b[10] = {0}; // массив b из 10 элементов, инициализированных 0
Если массив проинициализирован при объявлении, то константные начальные значения его элементов указываются через запятую в фигурных скобках. В этом случае количество элементов в квадратных скобках может быть опущено.
int a[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
При обращении к элементам массива индекс требуемого элемента указывается в квадратных скобках [].
Однако часто требуется задавать значения элементов массива в процессе выполнения программы. При этом используется объявление массива без инициализации. В таком случае указание количества элементов в квадратных скобках обязательно.
int a[10];
Для задания начальных значений элементов массива очень часто используется параметрический цикл:
Многомерные массивы
В языке Си могут быть также объявлены многомерные массивы. Отличие многомерного массива от одномерного состоит в том, что в одномерном массиве положение элемента определяется одним индексом, а в многомерном — несколькими. Примером многомерного массива является матрица.
Общая форма объявления многомерного массива
тип имя[размерность1][размерность2]...[размерностьm];
Элементы многомерного массива располагаются в последовательных ячейках оперативной памяти по возрастанию адресов. В памяти компьютера элементы многомерного массива располагаются подряд, например массив, имеющий 2 строки и 3 столбца,
int a[2][3];
будет расположен в памяти следующим образом
КоличествоСтрок * КоличествоСтолбцов = 2 * 3 = 6.
Количество байт памяти, требуемых для размещения массива, определится как
КоличествоЭлементов * РазмерЭлемента = 6 * 4 = 24 байта.
Инициализация многомерных массивов
Значения элементов многомерного массива, как и в одномерном случае, могут быть заданы константными значениями при объявлении, заключенными в фигурные скобки {}. Однако в этом случае указание количества элементов в строках и столбцах должно быть обязательно указано в квадратных скобках [].
Однако чаще требуется вводить значения элементов многомерного массива в процессе выполнения программы. С этой целью удобно использовать вложенный параметрический цикл.
Передача массива в функцию
Обработку массивов удобно организовывать с помощью специальных функций. Для обработки массива в качестве аргументов функции необходимо передать
- адрес массива,
- размер массива.
Исключение составляют функции обработки строк, в которые достаточно передать только адрес.
При передаче переменные в качестве аргументов функции данные передаются как копии. Это означает, что если внутри функции произойдет изменение значения параметра, то это никак не повлияет на его значение внутри вызывающей функции.
Если в функцию передается адрес переменной (или адрес массива), то все операции, выполняемые в функции с данными, находящимися в пределах видимости указанного адреса, производятся над оригиналом данных, поэтому исходный массив (или значение переменной) может быть изменено вызываемой функцией.
Пример на Си Дан массив из 10 элементов. Поменять местами наибольший и начальный элементы массива. Для операций поиска максимального элемента и обмена использовать функцию.
1. #define _CRT_SECURE_NO_WARNINGS
2. #include <stdio.h>
3. // Функция обмена
4. void change(int *x, int n)
5. {
6. // x - указатель на массив (адрес массива)
7. // n - размер массива
8. int i;
9. int max, index;
10. max = x[0];
11. index = 0;
12. // Поиск максимального элемента
13. for (i = 1; i<n; i++)
14. {
15. if (x[i]>max)
16. {
17. max = x[i];
18. index = i;
19. }
20. }
21. // Обмен
22. x[index] = x[0];
23. x[0] = max;
24. }
25. // Главная функция
26. int main()
27. {
28. int a[10];
29. int i;
30. for (i = 0; i<10; i++)
31. {
32. printf("a[%d] = ", i);
33. scanf("%d", &a[i]);
34. }
35. change(a, 10); // вызов функции обмена
36. // Вывод элементов массива
37. for (i = 0; i<10; i++)
38. printf("%d ", a[i]);
39. getchar();
40. getchar();
41. return 0;
42. }
Переполнение массива
Пускай у вас есть такой код
1. int A[10];
2. int i;
3.
4. for (i=0; i<=10; i++) {
5. A[i] = 1;
6. }
Здесь цикл for задан с ошибкой. В некоторых старых версиях компиляторов этот код зацикливался. Дело в том, что переменная i располагалась при компиляции сразу за массивом A. При выходе за границы массива счётчик переводился в 1.
Массивы небезопасны, так как неправильная работа с индексом может приводить к доступу к произвольному участку памяти (Теоретически. Современные компиляторы сами заботятся о том, чтобы вы не копались в чужой памяти).
Если вы работаете с массивами, то необходимо следить за тем, чтобы счётчик не превышал размер массива и не был отрицательным. Для этого, как минимум,
- 1. Используйте тип size_t для индексирования. Он обезопасит вас от отрицательных значений и его всегда хватит для массива любого размера.
- 2. Помните, что массив начинается с нуля.
- 3. Последний элемент массива имеет индекс (размер массива - 1)
Никаких полноценных способов проверки, вышли мы за пределы массива или нет, не существует. Поэтому либо мы точно знаем его размер, либо храним в переменной и считываем при надобности
Размер массива
Массив в си должен иметь константный размер. Это значит, что невозможно, например, запросить у пользователя размер, а потом задать этот размер массиву.
printf("Enter length of array ");
scanf("%d", &length);
{
float x[length];
}
Создание динамических массивов будет рассмотрено дальше, при работе с указателями и памятью
В некоторых случаях можно узнать размер массива с помощью функции sizeof.
- #include <conio.h>
- #include <stdio.h>
- void main() {
- int A[57];
- //sizeof возвращает размер всего массива в байтах
- //Для определения количества элементов необходимо
- //разделить размер массива на размер его элемента
- int size = sizeof(A) / sizeof(int);
- printf("Size of array equals to %d", size);
- getch();
- }
Но это вряд ли будет полезным. При передаче массива в качестве аргумента функции будет передаваться указатель, поэтому размер массива будет невозможно узнать.
Статические массивы удобны, когда заранее известно число элементов. Они предоставляют быстрый, но небезопасный доступ до элементов.