Операция * соответствует словам «значение, расположенное по указанному адресу».

Лабораторная работа № 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];