Практика 4
Указатели, ссылки, массивы
Указатели используются для доступа к областям памяти, содержащим значения переменных. Указатель – это переменная, значением которой является адрес памяти.
Объявление и использование указателей
Поскольку указатель – это переменная, его необходимо объявлять.
При объявлении указателей необходимо указывать тип данных, на которые эти указатели могут ссылаться. Это необходимо для реализации операций с адресами. При объявлении указателя используется оператор * (звездочка), который указывается перед именем указателя
Синтаксис объявления указателей
<тип данных>▬ * <имя указателя>
Здесь и далее символ ▬ при записи формата команды обозначает пробел.
При работе с указателями часто используются две операции:
& - определение адреса ячейки по ее имени;
* - определение значения, записанного по указанному адресу.
Адресная арифметика и сравнение указателей
Один указатель можно присваивать в качестве значения другому указателю.
При работе с указателями допустимы только арифметические операции сложения и вычитания. Если к указателю прибавить целое число N, то получим новый адрес. Его значение таково, что в памяти между исходной ячейкой и ячейкой, определяемой новым адресом, может разместиться N элементов базового типа. Результатом вычитания двух адресов (указателей) является целое число, указывающее, сколько элементов базового типа может разместиться между ячейками с соответствующими адресами. Указатели можно сравнивать как обычные переменные.
Многоуровневая адресация
Можно создать указатель на указатель

В верхней части рисунка представлена схема стандартной, одноуровневой адресации: переменная-указатель через свое значение-адрес ссылается на обычную переменную. Нижняя часть рисунка иллюстрирует двухуровневую адресацию: на переменную ссылается указатель, а на этот указатель, в свою очередь, ссылается еще один указатель. Могут быть трехуровневая, четырехуровневая и т. д. адресации. На практике редко используется адресация уровня больше двух.
При объявлении переменной указателя на указатель перед именем этой переменной указывают два оператора *. Каждый дополнительный уровень адресации означает наличие дополнительного оператора * в объявлении указателя.
Ссылки
В С++ существует неявная форма указателя – ссылка. По своей природе ссылка схожа с указателем, однако, в отличие от указателя при работе с ссылкой пользователь не получает прямого доступа к адресу ячейки памяти. В некотором смысле ссылка Ї это указатель, но только со скрытым адресом. Способ объявления и использования ссылки в программе полностью отличается от способов работы с указателем. Ссылки используются при передаче аргументов функциям, при возвращении значения функций через ссылки, а также при создании псевдонимов объектов (независимая ссылка). Независимая ссылка – это еще одно название для переменной в программе. Значение ссылки – это значение переменной, на которую ссылка выполнена. При объявлении ссылки необходимо сразу указать, на какую переменную она ссылается (инициализация ссылки). Ссылка инициализируется только один раз. При объявлении (и инициализации) ссылки перед ее именем указывается оператор &.
Статические одномерные массивы
Массив – совокупность элементов одинакового типа, объединенных общим именем. Переменные входящие в состав массива, называются элементами массива. Доступ к элементам массива осуществляется путем индексирования. Размерность массива определяется количеством индексов, необходимых для однозначного определения элемента массива. Массивы бывают статические (размер известен при компиляции программы) и динамические (размер определяется при выполнении программы). На данной практике рассматриваются статические массивы.
Под одномерным подразумевают массив, для индексации элементов которого используют только один индекс.
Синтаксис объявления массива
<тип данных>▬<имя массива>[<размер массива>]
Обращение к элементу массива выполняется через имя массива с индексом элемента в квадратных скобках. Индексация элементов в С++ начинается с нуля.
Ячейки памяти, в которые заносятся значения элементов массива, размещены рядом, то есть являются смежными.
Указатель на массив
Имя массива (без индексов) является указателем на первый элемент массива. Поэтому, зная адрес первого элемента и количество элементов мы получаем доступ ко всему массиву. Хотя доступ может быть получен и через имя массива и индекс элемента, арифметические операции с адресами выполняются быстрее, по сравнению с индексированием массива. Кроме того, указатель, который ссылается на массив, можно индексировать точно так же, как если бы это было имя массива.
Поскольку имя массива само является указателем на первый элемент массива можно обойтись без указателя p в приведенном выше примере.
Двумерные массивы
При объявлении многомерного массива указывается размер по каждому индексу. Для каждого индекса используется собственная пара квадратных скобок.
Среди многомерных массивов самым простым является двумерный массив.
При работе с двумерными массивами могут использоваться указатели. Однако в этом случае важно помнить о том, что такое на самом деле двумерный массив и как для его элементов выделяется память. Двумерный массив в С++ это обычный массив, элементами которого, в свою очередь, являются массивы. Размерность первого, «базового» массива при объявлении определяется числом в первых квадратных скобках, а размерность массивов-элементов Ї число во вторых квадратных скобках. Таким образом, формально указав имя массива и только первый индекс элемента, получаем ссылку на соответствующую строку двумерного массива. Более точно, название двумерного массива с одним лишь первым индексом есть не что иное, как указатель на первый элемент соответствующей строки массива.
В приведенном листинге для того, чтобы выдержать форматирование выводимых данных (числа должны располагаться строго по вертикали и горизонтали, без сдвигов из-за различной ширины чисел), использована команда printf("%4d",n[i][j]). Первый аргумент функции printf() "%4d"означает, что для вывода каждого числа используется 4 позиции, второй аргумент Ї выводимое на экран значение.
Как и при работе с двумерными массивами, в двумерных массивах само по себе имя массива является ссылкой на первый элемент массива. Однако в случае с двумерными массивами есть одна существенная особенность. Если, например, объявить целочисленный указатель и затем попытаться присвоить ему в качестве значения имя массива, при компиляции программы получим сообщение об ошибке. Проблема связана с несоответствием типов. Технически двумерный массив Ї это массив массивов. Имя одномерного массива Ї это адрес первого элемента. Первым элементом в двумерном массиве является строка с нулевым индексом. С точки зрения языка С++ эта строка есть массив. Поэтому имя двумерного массива является указателем со значением-адресом первого элемента в первой строке (элемент с двумя нулевыми индексами, в рассмотренном примере это n[0][0]), но тип этого указателя Ї массив (целых чисел для случая целочисленного двумерного массива).
Корректным будут, например, такие команды: int n[4][5], *q для объявления двумерного массива и указателя q, а присваивание значения указателю q реализуем командой q=(int *)n, в которой использована инструкция (int *) явного приведения типов.
Инициализация массивов
В С++ существует возможность инициализации массивов при их объявлении.
Синтаксис инициализации одномерного массива
<тип данных>▬<имя массива>[<размер массива>]={<значение 1>,<значение 2>,…}
Если массив инициализируется при объявлении, указывать размер массива необязательно (хотя квадратные скобки должны присутствовать). В этом случае размер массива определяется автоматически по числу значений в списке.
Также инициализируются двумерные массивы. При этом присваивание значений элементам массива выполняется построчно. Часто для удобства значения элементов каждой строки заключаются в фигурные скобки. При инициализации массивов, если явно указан их размер, недостающие элементы заполняются нулями.
Используя приведенные особенности инициализации массива, для инициализации двумерного массива нулевыми значениями достаточно использовать команду вида int n[3][4]{0};
Массивы символов
В C++ не существует встроенного типа для текстовых данных. Текстовые строки реализуются в виде массивов символов либо в виде объектов класса string. При использовании массива символов необходимо зарезервировать достаточно места для того, чтобы в него можно было записывать строки различной длины. В качестве индикатора, указывающего, где в массиве заканчивается полезная информация, используется специальный символ ‘\0’, который называется нуль-символом. Чтобы вписать в массив строку необходимо, чтобы его размер, по крайней мере на единицу, превышал размер строки.
Если инициализация символьного массива выполняется путем указания текстового значения (значение заключено в двойные кавычки), нуль-символ добавляется автоматически.
Если рассмотреть два способа инициализации массива при объявлении
char str1[] = ″hello″;
char str2[] = { ′h′,′e′,′l′,′l′,′o′,};
то размер массива в первом случае будет 6, во втором -5.
Массивы указателей
Элементами массивов могут быть указатели. Синтаксис объявления массива указателей:
<тип данных>▬ * <имя массива>[<размер массива>]
С помощью массива указателей можно создавать массивы переменной размерности: в двумерном массиве каждая строка содержит разное количество элементов.


