Лабораторная работа № 14
«Массивы и указатели. Многомерные массивы»
Цель работы: Изучить одномерные и двумерные массивы, уметь объявлять одномерные и двумерные массивы в программе, обращаться к элементу массива, работать с индексными переменными.
.
Теоретические сведения
Массивом называется набор данных одного и того же типа, собранных под одним именем. Каждый элемент массива определяется именем массива и порядковым номером элемента, который называется индексом. Индекс всегда является целым числом.
Основная форма объявления массива в программе:
Тип < имя массива > [размер 1] [размер 2]….[размер n];
Чаще всего используются одномерные массивы:
Тип <имя массива> [размер];
Тип – базовый тип элементов (int, float, char).
Размер – количество элементов одномерного массива.
В языке Си индекс всегда начинается с 0. Первый элемент - массива всегда имеет индекс 0. Например, если мы объявили массив int a [100], это значит массив содержит 100 элементов – от a [0] до a [99]. Для одномерного массива легко подсчитать, сколько байт в памяти будет занимать этот массив.
N Килобайт = < размер базового типа > * < количество элементов >.
Язык Си допускает двумерные массивы. Их можно назвать как: массив одномерных массивов. Двумерный массив int a[3][4] можно представить в виде таблички:
Hoмер столбца – второй индекс (j) | ||||
Номер строки – первый индекс (i) | A [0] [0] | A [0] [1] | A [0] [2] | A [0] [3] |
A [1] [0] | A [1] [1] | A [1] [2] | A [1] [3] | |
A [2] [0] | A [2] [1] | A [2] [2] | A [2] [3] |
В памяти ЭВМ массив располагается непрерывно по строкам, т. е.
a [0] [0], a [0] [1], ……. a [0] [3], a [1] [0], a [1] [1], …..a [2] [3].
Следует помнить, что память для всех массивов, которые определены как глобальные, отводится во время компиляции, и сохраняется всё время, пока работает программа. Часто двумерные массивы используются для работы с таблицами, содержащими текст.
Инициализацию массивов можно производить разными способами.
Первый способ.
float arr [6] = {1.1, 2.2, 3.3, 4.0, 5.0, 6}; // одномерный массив
int a [3] [5] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
Второй способ.
a[0] [0] = 1; a [0] [1] = 2; a [0] [2] = 3; a [0] [3] = 4
a [0] [4] = 5; a [0] [5] = 6; a [0] [6] = 7; a [0] [7] = 8 и т. д.
Многомерные массивы, в том числе и двумерные, можно инициализировать, рассматривая их как массив массивов.
Инициализации:
int a [3] [5] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; и
int a [3] [5] = {{1, 2, 3, 4, 5}, {6, 7, 8, 9, 10}, {11, 12, 13, 14, 15}};
эквиваленты. Количество инициализаторов не обязательно должно совпадать с количеством элементов массива. Если инициализаторов меньше, то оставшиеся элементы массива не определены.
В тоже время инициализации:
int a [3] [5] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}; и
int a [3] [5] = {{1, 2, 3}, {4, 5, 6, 7, 8}, {9, 10, 11}};
различны.
Соответствующие массивы будут заполнены следующим образом:
В первом случае:
Во втором случае:
В пустых клетках значения не определяются.
Символьные массивы могут инициализироваться как обычные массивы
char str [15] = {‘B’, ‘o’, ‘r’, ‘l’, ‘a’, ‘n’, ‘d’, ‘ ‘, ‘C’, ‘+’, ‘+’};
а могут как строка:
сhаr str [15] = ‘Borland C + +’;
Можно указывать массив без указания размера:
int mas [ ] = {1, 2, 3, 4, 5, 1, 2};
char str [ ] = ”Это объявление и инициализация символов”;
компилятор сам определит количество элементов массива. Символьные массивы в языке Си занимают особое место. Во многих языках есть специальный тип – строка символов. В языке Си этого нет, и работа со строками реализована как работа с одномерным массивом.
Указатели – это переменные, которые содержат в качестве своих значений адреса памяти компьютера. Вообще-то это просто целое число. Но нельзя трактовать указатель как переменную или константу целого типа. Если переменная будет указателем, то она должна быть соответствующим образом объявлена.
Указатель объявляется следующим образом:
тип *<имя переменной>;
Например:
char *ch;
int * temp, i, *j;
float * pf, f;
Здесь объявлены указатели ch, temp, j, pf, переменная i – целого типа и переменная f типа float.
Операции над указателями. С указателями связаны две специальные операции & и *. Обе эти операции являются унарными, т. е. они имеют один операнд, перед которыми они ставятся.
Операция & соответствует операции «взять адрес».
Операция * соответствует словам «значение, расположенное по указанному адресу».
В указателе очень важным является базовый тип, т.к. он определяет, сколько байтов занимает переменная указатель. Если int – 2 байта, char – один байт и т.д. Простейшие действия с указателями продемонстрируем на следующей программе.
Пример
# include <stdio.h>
main ()
{
float x = 10.1, y;
float * pf;
pf = &x;
y = *pf;
printf(“x = %f y= %f”, x, y); // Результат: x=10.1; y=10.1; pf=FFF6
*pf ++;
printf (“x = %f y = %f”, x, y); // Результат: x=10.1; y=10.1; pf=FFF2
y =1+ *pf * y;
printf (“x = %f y = %f”, x, y); // Результат: x=10.1; y=1; pf=FFF6
return 0;
}
К указателям можно применить операцию присваивания. Указатели одного и того же типа могут использоваться в операции присваивания, как любые другие переменные.
Пример
# include <stdio.h>
main ()
{
int x = 0;
int *p, *g;
p = &x;
g = p;
printf(“%p”, p); /* печать содержимого p */
printf(“%p”, g); /* печать содержимого g */
printf(“%d %d”, x., *g); / *печать величины х и величины по адресу g */
}
В этом примере приведена еще одна спецификация формата функции printf() %p – печать адреса памяти в шестнадцатеричной системе счисления.
В языке Си указателю допустимо присвоить любой адрес памяти. Однако если объявлен указатель на целое int *pi; а по адресу, который присвоен данному указателю, находится переменная типа float, то при компиляции будет выдано сообщение об ошибке в строке
p = &x;
Эту ошибку можно исправить, преобразовав указатель на int к типу указателя на float явным преобразованием типа:
p = (int*)&x;
но при этом теряется информация о том, на какой тип указывал исходный указатель.
Массивы описывают регулярную структуру данных одного типа.
Одномерные массивы:
int temp [365];
char arr [10];
char *point[10];
Двумерные массивы:
int array[4] [10];
char arr [3] [7];
Число в [ ] указывает количество элементов массива, поэтому:
temp [365] – массив из 365 элементов.
Доступ к каждому элементу осуществляется по его индексу (номеру), т.е. temp[0], temp[1],…,temp[364]- последний элемент. Элементы массива нумеруются начиная с 0.
Можно также использовать многомерные массивы, например: int arr [k] [l] …[n];
Однако следует помнить, что для хранения элементов таких массивов требуется значительный объем памяти.
Рассмотрим, как происходит размещение элементов массива в памяти ЭВМ. Как правило, элементы массива занимают последовательные ячейки памяти. При этом элементы размещаются таким образом, что самый последний индекс возрастает быстрее. Это в случае двумерного массива означает, что он будет записываться построчно: строка за строкой. Поскольку указатели указывают адрес ячейки, то между массивами и указателями существует тесная связь. Вспомним, что имя массива – это указатель на его первый элемент. По существу массив можно рассматривать как индексированный указатель. Доступ к элементам массива осуществляется по номеру индекса. При этом приращение индекса на единицу вызывает перемещение указателя на число байт, соответствующее объекту данного типа: для целых чисел на 2 байта, для действительных - на 4 байта и т.д.
Объявления int mas[] и int *mas идентичны по действию: оба объявляют mas указателем.
Индекс массива действует аналогично стрелки часов, показывающей по очереди на каждый следующий элемент массива.
Пример
int mas[10];
int *ptr;
ptr = mas; // присваивает адрес указателю
// следующие операции дадут один и тот же результат:
mas[2] = 20;
*(ptr + 2) = 20;
// следующая операция прибавит 2 к первому элементу:
*ptr + 2;
Указатели и многомерные массивы. Рассмотрим двумерный массив и действия с указателями.
int mas[4][2];
int *ptr;
ptr = mas;
ptr сейчас указывает на первый столбец первой строки, т.е.
ptr = = mas = = &mas [0] [0];
Увеличим указатель:
ptr+1 = = &mas [0] [1];
ptr+2 = = &mas [1] [0];
ptr+3 = = &mas [1] [1] и т.д.
Двумерный массив можно представить как массив массивов. В нашем случае мы имеем четырех элементный массив, состоящий из двух элементов. Примечательно, что этот четырех элементный массив можно представить в виде одномерного mas[0],…,mas[3]. При этом имя массива по-прежнему является указателем на его первый элемент, т.е. mas[0]= =&mas[0] [0]. На что же будут указывать mas[i]? В этом случае mas [i] указывает на i-тую строку, т.е. на первый элемент i - й строки. Таким образом
mas [0] == &mas [0] [0];
mas [1] == &mas [1] [0];
mas[2] == &mas [2] [0];
mas[3] == &mas [3] [0];