Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Разработка ПО на языке Си для микроконтроллера AT91SAM7S.pdf
Скачиваний:
120
Добавлен:
18.05.2014
Размер:
838.69 Кб
Скачать

4Массивы

4.1Основные сведения

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

Определение массива в программе приводит к выделению неразрывного блока памяти определённого размера. Адрес этого блока памяти и его размер являются константами, т. е. после определения невозможно сопоставить массив другому блоку памяти, или изменить его размер. Примеры определения массивов приведены в листинге 8.

Применение оператора sizeof к массиву возвращает объём памяти, который занимает данный массив (но не количество элементов в массиве). Для определения количества элементов в массиве необходимо поделить размер массива в памяти на размер одного элемента массива (пример см. в листинге 8).

Обращение к элементам массива происходит по индексу, указанному в квадратных скобках после имени массива. Индексация начинается с нуля (т. е. первому элементу одномерного массива соответствует индекс 0). Таким образом, в массиве определённом как «int array[20]» первый элемент будет иметь индекс 0, а последний – 19. Проверка выхода за границы массива во время работы программы не осуществляется – об этом должен заботиться программист! Попытка обращения к элементу за пределами массива приведёт к непредсказуемым последствиям.

Многомерные массивы в Си представляются как массивы массивов.

Листинг 8. Пример определения массивов.

//Массив, содержащий 3 элемента (без инициализации значений) short vector[3];

//Определение массива с одновременным его заполнением.

//В этом случае компилятор может сам определить размер массива, поэтому

//указывать его не обязательно (в данном случае будет равен 8)

int array[] = {1, 2, 3, 4, 5, 10, 20, 100500};

// Массив из 2 строк, каждая из которых содержит 3 элементa типа short short matrix[2][3] = {

{1, 2, 3},

17

{11, 12, 13} };

//Массив строк, каждая из которых содержит 5 элементов типа int

//Кол-во строк определяется компилятором автоматически (в данном случае

//равно 3)

int matrix2[][5] = {

 

 

 

 

{10,

20,

30,

40,

50},

{71,

72,

73,

74,

77},

{1, 2, 103, 174, -80} };

int main (void)

{

// Обращение (запись) к первому элементу массива

//(имеет нулевой индекс) vector[0] = 100;

//Создаём указатель на первый элемент массива

//Эквивалентно short *ptrToVector= &vector[0]; short * ptrToVector = vector;

*ptrToVector = 200; // было 100, станет 200

//matrix - двумерный массив размером 2 строки на 3 столбца,

//поэтому matrix[1] является одномерным массивом-строкой, имеющим

//размер, равный 3.

//Через массив легко сформировать указатель на первый элемент

//массива. В данном случае это будет указатель на первый элемент

//строки с индексом 1.

short * row = matrix[1];

//С указателем можно работать как с массивом row[2] = 100; // было 13, станет 100

//Для того, чтобы определить кол-во элементов в массиве, делим

//размер всего массива на размер одного элемента.

const int elementsInArray = sizeof(array)/sizeof(array[0]); // =8

//Поскольку многомерные массивы - это массивы массивов, то

//для двумерного массива "элементом" является одномерный массив,

//представляющий строку.

const int rows = sizeof(matrix) / sizeof (matrix[0]);// =3

const int columns = sizeof (matrix[0])/sizeof (matrix[0][0]);// =5

return 0;

}

18

4.2Указатель на первый элемент массива

Массив не является указателем, однако из него легко формируется указатель, который будет ссылаться на первый элемент массива (т. е. на начало области памяти занимаемой массивом). Для этого достаточно проинициализировать указатель именем массива (см. листинг 8).

Вызов некоторой функции с указанием имени массива в качестве её аргумента приводит к неявному созданию указателя на первый элемент этого массива и передаче его в функцию (см. листинг 9). Вследствие этого, применение sizeof к указателю на элемент массива работает не так, как в случае с применением sizeof к массиву. Даже если аргумент в сигнатуре функции выглядит как массив, он всё равно является указателем! Такая форма записи используется лишь чтобы подчеркнуть, что функция работает с переданным указателем как с массивом, состоящим из нескольких значений, а не одной единственной переменной, на которую ссылается указатель.

Листинг 9. Передача указателя на массив в функцию

//Массив из 3х элементов short vector[3];

//Функция принимает указатель, а не массив!

//Эквивалентно void zeroVector (short * m) void zeroVector (short m[])

{

//Всегда равно 4 для AT91SAM7S, т.к. указатель занимает

//в памяти 4 байта!

int sz = sizeof (m);

//Несмотря на то, что m здесь – указатель,

//с ним можно работать как с массивом m[0] = 0;

m[1] = 0; m[2] = 0;

//m[3] = 0; // Некорректно, доступ за пределы массива.

}

int main (void)

{

//Здесь происходит неявное создание указателя

//на первый элемент массива и передача его в качестве

//параметра в функцию zeroVector

zeroVector (vector);

}

19

4.3Работа с указателями как с массивами

Как можно заметить из листинга 9 в функцию zeroVector передаётся указатель на первый элемент массива, однако далее работа с этим указателем происходит как с массивом. Такая возможность предусмотрена языком Си.

4.4Арифметика указателей

Куказателям применимы операции сложения с целыми числами и вычитания (в т. ч. инкремент и декремент). Однако работают они не так, как в случае с обычными переменными. Прибавление к указателю единицы приводит к увеличению адреса, хранящегося в указателе, на величину, равную размеру связанного с указателем типа. Таким образом, инкремент указателя типа 'unsigned int *' приведёт к увеличению его адреса на 4, поскольку тип 'unsigned int' занимает в памяти четыре байта. Аналогично, декремент приводит к уменьшению адреса на ту же величину. Смысл такого поведения заключается в следующем: инкремент или декремент указателя соответствуют перемещению указателя на следующий или предыдущий элемент связанного с указателем типа в памяти.

Листинг 10. Применение арифметических операций к указателям

//Массив из 20 целочисленных переменных unsigned int array[20];

//Создаём указатель на первый элемент массива. unsigned int * first = array;

//Указатель на 3й элемент массива (имеет индекс 2). unsigned int * third = first + 2;

//Указатель на последний элемент массива (имеет индекс 19) unsigned int * ptr = third + 17;

unsigned int value = *ptr; // Значение последнего элемента массива; unsigned int value2 = array[19]; // Тоже значение последнего элемента.

Как можно заметить из листинга, array[i] эквивалентно *(array + i). При этом, array в данном выражении может являться как указателем на массив, так и именем массива.

4.5Разноразмерные массивы

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

20

void func(int i)

{

//Размер массива будет зависеть от значения аргумента i;

//При каждом вызове функции массив будет создаваться в памяти,

//а при выходе – уничтожаться.

short array[i];

...

}

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

21