Массивы в языке СИ

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

Элемент массива (значение элемента массива) – значение, хранящееся в определенной ячейке памяти, расположенной в пределах массива, а также адрес этой ячейки памяти.
Каждый элемент массива характеризуется тремя величинами:

  • адресом элемента — адресом начальной ячейки памяти, в которой расположен этот элемент;
  • индексом элемента (порядковым номером элемента в массиве);
  • значением элемента.

У каждого элемента в массиве есть свое положение — индекс элемента, т.е. его порядковый номер. Индекс элемента массива не имеет никакого отношения к присвоенному ему значению.

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

Адрес массива – адрес начального элемента массива.

Имя массива – идентификатор, используемый для обращения к элементам массива.

Размер массива – количество элементов массива

Размер элемента – количество байт, занимаемых одним элементом массива.

Графически расположение массива в памяти компьютера можно представить в виде непрерывной ленты адресов.

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

Представленный на рисунке массив содержит 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;

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

В языке Си могут быть также объявлены многомерные массивы. Отличие многомерного массива от одномерного состоит в том, что в одномерном массиве положение элемента определяется одним индексом, а в многомерном — несколькими. Примером многомерного массива является матрица.

Общая форма объявления многомерного массива

тип имя[размерность1][размерность2]...[размерностьm];

Элементы многомерного массива располагаются в последовательных ячейках оперативной памяти по возрастанию адресов. В памяти компьютера элементы многомерного массива располагаются подряд, например массив, имеющий 2 строки и 3 столбца,

int a[2][3];

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

Общее количество элементов в приведенном двумерном массиве определится как:

КоличествоСтрок * КоличествоСтолбцов = 2 * 3 = 6.

Количество байт памяти, требуемых для размещения массива, определится как:

КоличествоЭлементов * РазмерЭлемента = 6 * 4 = 24 байта.

  
целочисленный массив из 8 элементов с именем arr_int
вещественный массив из 11 элементов с именем arr_float
символьный массив из 6 элементов с именем arr_char
У массива, как и у переменной, имеются свои имя и тип данных. Кроме того, у массива ещё есть одна дополнительная характеристика – размер массива. Размер массива – количество элементов, которые могут в нём храниться. В нашей аналогии с коробочками это количество коробок.
целочисленный массив из 8 элементов с именем arr_int вещественный массив из 11 элементов с именем arr_float символьный массив из 6 элементов с именем arr_char У массива, как и у переменной, имеются свои имя и тип данных. Кроме того, у массива ещё есть одна дополнительная характеристика – размер массива. Размер массива – количество элементов, которые могут в нём храниться. В нашей аналогии с коробочками это количество коробок.

Работа с отдельными элементами массива

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

Давайте, например, выведем элементы массива из пяти элементов на экран.

Листинг 5.

#include <stdio.h>

int main(void){
int arr[5] = {2, 4, 3, 5, 5};

printf("%d %d %d %d %d\n",arr[0], arr[1], arr[2], arr[3], arr[4]);
return(0);
}

Конечно, если массив будет очень большой, то выводить его поэлементно подобным образом то ещё удовольствие. Да и с маленькими массивами так никто не делает. Лучше и правильнее использовать циклы. а картинке выше изображено три массива:

Размер массива

Массив в си должен иметь константный размер. Это значит, что невозможно, например, запросить у пользователя размер, а потом задать этот размер массиву.

printf("Enter length of array ");

scanf("%d", &length);

{

float x[length];

}

Создание динамических массивов будет рассмотрено дальше, при работе с указателями и памятью
В некоторых случаях можно узнать размер массива с помощью функции
sizeof.

  1. #include <conio.h>
  2. #include <stdio.h>
  3. void main() {
  4. int A[57];
  5. //sizeof возвращает размер всего массива в байтах
  6. //Для определения количества элементов необходимо
  7. //разделить размер массива на размер его элемента
  8. int size = sizeof(A) / sizeof(int);
  9. printf("Size of array equals to %d", size);
  10. getch();
  11. }

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

Переполнение массива

Пускай у вас есть такой код

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)

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

Передача массива в функцию

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

  • адрес массива,
  • размер массива.

Исключение составляют функции обработки строк, в которые достаточно передать только адрес.

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

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

Пример на Си Дан массив из 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. }