Конструктор двумерного массива c

Обновлено: 17.04.2024

Для хранения прямоугольных таблиц, состоящих из строк и столбцов, необходимо использовать массивы, каждый элемент которых является строкой массивом чисел. Если считать, что одна строка — это vector , то двумерный массив — это вектор элементов типа vector , то есть его нужно объявлять как vector . При этом по стандарту языка C++ до 2011 года, в конце определения между двумя символами “

Если объявить массив a таким образом, то a[i] будет одномерным массивом, который обычно считают строкой. То есть a[i][j] будет j-м элементом i-й строки. Например, двумерный массив из 3 строк и 4 столбцов можно записать в виде:

Чтобы создать массив из n строк и m столбцов, можно объявить его указанным образом:

Затем необходмио размер “внешнего” массива изменить на n (сделать n строк в таблице):

Затем размер каждого массива-строки необходимо изменить на m. Это можно сделать циклом:

Заметим, что строки могут иметь разную длину, например, можно сделать массив в форме “лесенки”, где каждая строка будет содержать на один элемент больше предыдущей:

Но если необходимо создать прямоугольный массив, то можно сразу же при объявлении задать его размер, если воспользоваться конструктором для вектора с параметрами. Первый параметр — размер вектора, второй необязательный параметр — значение, которым будут инциализированы элементы вектора. Тогда в качестве первого параметра можно передать n, а в качестве второго параметра можно явно указать конструктор, который создает вектор из m элементов типа int : vector(m) . Итак, создать прямоугольную таблицу размером n×m можно в одну строку:

Если вложенному вызову конструктора (для строки) передать второй параметр, то все элементы массива будут заполнены переданным значением вместо нуля. int A[n] создает в памяти одномерный массив: набор пронумерованных элементов, идущих в памяти последовательно. К каждому элементу массива можно обратиться, указав один индекс - номер этого элемента. Но можно создать и двумерный массив следующим образом: int A[n][m] . Данное объявление создает массив из n объектов, каждый из которых в свою очередь является массивом типа int [m] . Тогда A[i] , где i принимает значения от 0 до n-1 будет в свою очередь одним из n созданных обычных массивов, и обратиться к элементу с номером j в этом массиве можно через A[i][j] .

Подобные объекты (массивы массивов) также называют двумерными массивами. Двумерные массивы можно представлять в виде квадратной таблицы, в которой первый индекс элемента означает номер строки, а второй индекс – номер столбца. Например, массив A[3][4] будет состоять из 12 элементов и его можно записать в виде -->

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

А считать двумерный массив с клавиатуры можно при помощи еще более простого алгоритма (массив вводится по строкам, то есть в порядке, соответствующему первому примеру):

Обработка двумерного массива

Обработка двумерных массивов производится аналогичным образом. Например, если мы хотим записать в массив таблицу умножения, то есть присвоить элементу a[i][j] значение i * j , это можно сделать следующим образом при помощи вложенных циклов:

Рассмотрим более сложную задачу и несколько способов ее решения. Пусть дан квадратный двумерный массив размером n×n. Необходимо элементам, находящимся на главной диагонали проходящей из левого верхнего угла в правый нижний (то есть тем элементам a[i][j] , для которых i == j ) присвоить значение 1 , элементам, находящимся выше главной диагонали – значение 0, элементам, нахощящимся ниже главной диагонали – значение 2. То есть получить такой массив (пример для n == 4 ):

Рассмотрим несколько способов решения этой задачи. Элементы, которые лежат выше главной диагонали – это элементы a[i][j] , для которых i < j , а для элементов ниже главной диагонали i >j . Таким образом, мы можем сравнивать значения i и j и по ним определять значение a[i][j] . Получаем следующий алгоритм:

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

Сначала заполним главную диагональ, для чего нам понадобится один цикл:

Затем заполним значением 0 все элементы выше главной диагонали, для чего нам понадобится в каждой из строк с номером i присвоить значение элементам a[i][j] для j = i+1 , . n-1 . Здесь нам понадобятся вложенные циклы:

Аналогично присваиваем значение 2 элементам a[i][j] для j = 0 , . i-1 :

Можно также внешние циклы объединить в один и получить еще одно, более компактное решение:

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

Если необходимо хранить в массиве величину, зависящую от трёх параметров-индексов, например, a[i][j][k] , то для представления такого массива нужно использовать вектор, элементами которого будут двумерные векторы, то есть

Их можно рассматривать как “трёхмерные” таблицы, но проще думать о таких массивах, просто как о величине, определяемой тремя параметрами-индексами.

Их тоже можно создавать в одну строку, добавив ещё один уровень вложенности конструкторов. Например, пусть требуется создать массив размера x×y×z, то есть первый индекс будет принимать значения от 0 до x-1, второй индекс от 0 до y-1, третий индекс от 0 до z-1. Можно использовать такое объявление:

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

Передавать двумерные массивы в функцию всегда лучше по ссылке, т.е. добавляя знак “&” перед идентификатором параметра. Например:

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

Форматирование чисел при выводе

Допустим, мы заполним массив таблицей умножения: a[i][j] = i * j как в примере в начале раздела. Если мы теперь попробуем вывести этот массив на экран, разделяя элементы в строке одним пробелом, то из-за того, что числа имеют различную длину столбцы таблицы окажутся неровными:

Для того, чтобы получить ровные столбцы необходимо, выводить числа так, чтобы одно выводимое число имело ширину, например, ровно в 3 символа, а “лишние” позиции были бы заполнены пробелами. Тогда получится следующая таблица:

Для того, чтобы выводимое число или строка имело ровно заданную ширину, необходимо перед выводом его на экран для потока cout вызвать метод width с параметром 3 . Данный метод устанавливает ширину поля для выводимого значения. Получим следующую программу для вывода:

Заметим, что мы теперь не выводим пробел после каждого числа, поскольку мы добавили этот пробел к ширине выводимого поля. Функция width действует однократно, только на следующее выводимый в поток значение, поэтому ее нужно вызывать перед каждым выводом числа на экран.

Внимание! Если выводимое число или строка имеет большую длину, чем это было установлено функцией width , то это число или строка будут выведены полностью, а не будет обрезано до указанного значения. То есть предпочтительней вывести результат некрасиво, нежели неверно.

В первой статье были описаны приёмы работы с простейшим видом массивов — одномерным (линейным) массивом. В этой, второй статье будут рассмотрены многомерные массивы. В основном, речь пойдёт о двумерных массивах. Но приведённые примеры легко экстраполируются на массивы любой размерности. Также как и в первой статье, будут рассматриваться только массивы в стиле C/C++, без использования возможностей STL.

Эта статья предполагает у читателя базовые знания об одномерных и многомерных массивах, указателях и адресной арифметике. Почерпнуть эти знания можно в любом учебнике по C/C++.

Классика жанра

Если мы откроем классический труд «Язык программирования C» Брайана Кернигана и Денниса Ритчи, то прочитаем, что «В языке C есть возможность работать с многомерными прямоугольными массивами, хотя на практике они используются гораздо реже, чем массивы указателей». C++ практически полностью унаследовал работу с многомерными массивами своего предтечи.

Определение автоматических многомерных массивов

В этом разделе я буду иногда употреблять термин «матрица» как синоним термина «двумерный массив». В C/C++ прямоугольный двумерный массив чисел действительно реализует математическое понятие «матрица». Однако, в общем случае, двумерный массив — понятие гораздо более широкое, чем матрица, поскольку он может быть и не прямоугольным, и не числовым.

Определение автоматических многомерных массивов почти полностью совпадает с определением одномерных массивов (о чём было рассказано в первой статье), за исключением того, что вместо одного размера может быть указано несколько:

В этом примере определяется двумерный массив из 3 строк по 5 значений типа int в каждой строке. Итого 15 значений типа int .

Во втором примере определяется трёхмерный массив, содержащий 3 матрицы, каждая из которых состоит из 5 строк по 2 значения типа int в каждой строке.

Понятно, что тип данных, содержащихся в многомерном массиве, может быть любым.

При дальнейшем изложении для таких многомерных массивов будет употребляться термин «C-массив», что бы отличать их от массивов других видов.

Инициализация

При статической (определяемой на этапе компиляции) инициализации значения C-массива перечисляются в порядке указания размеров (индексов) в определении массива. Каждый уровень (индекс), кроме самого младшего, многомерного массива заключается в свою пару фигурных скобок. Значения самого младшего индекса указываются через запятую:

В примере показана статическая инициализация прямоугольного массива. Весь список инициализирующих значений заключён в фигурные скобки. Значения для каждой из 3 строк заключены в свою пару из фигурных скобок, значения для каждого из 5 столбцов для каждой строки перечислены через запятую.

При наличии инициализатора, самый левый размер массива может быть опущен. В этом случае компилятор сам определит этот размер, исходя из списка инициализации.

Заполнение массива значениями

Многомерный массив заполняется значениями с помощью вложенных циклов. Причём, как правило, количество циклов совпадает с размерностью массива:

В этом примере каждому элементу массива присваивается значение, первая цифра которого указывает номер строки, а вторая цифра — номер столбца для этого значения (нумерация с 1).

Вывод значений массива на консоль

В продолжение предыдущего примера можно написать:

В результате получим следующий вывод на консоль:

Для трёхмерного массива можно написать код, использующий те же приёмы:

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

Расположение в памяти

Для многомерного C-массива выделяется единый блок памяти необходимого размера: размер_массива1 * размер_массива2 * . * размер_массиваN * sizeof(тип_элемента_массива) .

Значения располагаются последовательно. Самый левый индекс изменяется медленнее всего. Т.е. для трёхмерного массива сначала располагаются значения для первой (индекс 0) матрицы, затем для второй и т.д. Значения для матриц располагаются построчно (ср. со статической инициализацией массива выше).

Имя (идентификатор) многомерного C-массива является указателем на первый элемент массива (так же как и для одномерных массивов)

Если код из последнего примера немного изменить:

поставить точку останова на return и посмотреть под отладчиком память, отведённую под переменную ary , то будет видно, что значения, расположенные в памяти, последовательно возрастают:

Поскольку все значения многомерного C-массива располагаются последовательно, то, пользуясь адресной арифметикой, можно сделать следующий хак:

В последнем фрагменте осуществляется доступ к значениям двумерного массива как к одномерному массиву. Цивилизованное решение реализуется через union .

Из двух примеров, приведённых выше, следует, что работу с двумерным или многомерным массивом (в понимании на более высоком уровне абстракции) технически можно организовать посредством одномерного массива соответствующего размера:

Этот приём достаточно распространён. Его выгода в том, что массив ary[DIM1 * DIM2] не обязательно должен быть выделен автоматически. Его можно выделять и динамически. Но при этом логически рассматривать как C-массив.

Вышеприведённый код написан в духе чистого C. В C++ обычно такие вещи прячут в класс, оставляя снаружи лаконичный интерфейс без всяких следов адресной арифметики.

Неродные близнецы

Теперь рассмотрим работу с «динамическими» многомерными массивами, т.е. с массивами, память для которых выделяется динамически.

Создание и уничтожение динамических многомерных массивов

Как правило, работа с такими массивами осуществляется следующим образом:

(1) Для доступа к двумерному массиву объявляется переменная ary типа указатель на указатель на тип (в данном случае это указатель на указатель на int ).

(2) Переменная инициализируется оператором new , который выделяет память для массива указателей на int .

(3) В цикле каждый элемент массива указателей инициализируется оператором new , который выделяет память для массива типа int .

Освобождение памяти происходит строго в обратном порядке: сначала уничтожаются массивы значений типа int , а затем уничтожается массив указателей.

Работа с динамическим многомерным массивом синтаксически полностью совпадает с работой с многомерным C-массивом.

Пример кода для трёхмерного массива:

Где собака порылась

Работа с динамическим многомерным массивом синтаксически полностью совпадает с работой с многомерным C-массивом. (Цитирую предыдущий раздел.) Синтаксически — да, но между этими массивами есть глубокое различие, о котором начинающие программисты часто забывают.

Во-первых, для динамического массива выделяется другой объём памяти.

Если посчитать, сколько памяти будет выделяться для двумерного массива из примера выше, то получится: первый оператор new выделил память для 3 указателей, второй оператор new в цикле трижды выделил память для 5 элементов типа int . Т.е. получилось, что выделили памяти для 15 значений типа int и для 3 значений типа указатель на int . Для C-массива компилятором была выделена память только для 15 значений типа int . (Всяческие выравнивания и прочие оптимизации не учитываем!)

Во-вторых, память, выделенная для динамического массива, не непрерывна. Следовательно, хак №1 (обращение с двумерным массивом как с одномерным) работать не будет.

В-третьих, передача многомерных массивов в функции и работа с ними будет отличаться для динамических массивов и C-массивов.

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

Динамический многомерный массив НЕ является C-массивом.

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

Стоит отметить, что массив указателей на массивы — структура более гибкая, чем двумерный C-массив. Например, для массива указателей на массивы размеры массивов могут быть разными, или какой-то массив может вообще отсутствовать. Наиболее распространённым примером является «массив строк», т.е. массив указателей на массивы типа char (пример — см. в следующем разделе).

Ещё раз о предосторожности

Из вышеизложенного следует, что нужно чётко отличать многомерные C-массивы вида

от массивов указателей на массивы.

Иногда внешние отличия весьма незначительны. К примеру С-строка — это одномерный массив элементов типа char , заканчивающийся нулевым байтом. Как реализовать массив строк?

Это — пример определения и инициализации двумерного C-массива

Каждая С-строка занимает ровно 10 байт, включая завершающий ноль (считаем, тип char имеет размер 1 байт). Неиспользуемые байты у коротких строк, вроде «May», содержат «мусор» (или нули, если об этом позаботился компилятор). Весь массив занимает один непрерывный блок памяти размером 120 байт (12 строк по 10 символов).

А здесь определён и инициализирован одномерный (!) массив указателей на массивы элементов типа char .

Вся информация, доступная через переменную month , занимает 13 блоков памяти: массив из 12 указателей и 12 блоков памяти, адреса которых хранятся в указателях, содержащих С-строки с названиями месяцев. И нет никакой гарантии, что 12 блоков памяти с С-строками будут расположены в памяти последовательно и в порядке, соответствующем перечислению в инициализаторе.

Но в обоих случаях доступ к символу b в строке «February» будет осуществляться выражением month[1][2] .

И, в заключение, ещё одно предостережение.

Поскольку многомерные C-массивы, как правило, занимают большой объём памяти, их надо с особой осторожностью объявлять внутри функций, в том числе в main() . И с осторожностью в n-ной степени в рекурсивных функциях. Можно легко получить переполнение стека и, как следствие, аварийное завершение программы.

Многомерные массивы при работе с функциями

Поскольку многомерные C-массивы и многомерные динамические массивы — совершенно разные типы данных, то и при работе с функциями подходы будут разные.

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

Функция, получающая C-массив в качестве параметра, может выглядеть следующим образом:

Форма (1) — наиболее распространённая.

Форма (2). При передаче многомерного C-массива в функцию можно не указывать длину самого левого измерения. Компилятору для расчёта доступа к элементам массива эта информация не нужна.

Как всегда в C/C++, параметр передаётся в функцию по значению. Т.е. в функции доступна копия фактического параметра. Поскольку имя C-массива является указателем на его первый элемент (т.е. адресом первого элемента), то в функцию передаётся копия адреса начала массива. Следовательно, внутри функции можно изменять значения элементов массива, т.к. доступ к ним осуществляется через переданный адрес, но нельзя изменить адрес начала массива, переданный в качестве параметра, т.к. это — копия фактического параметра.

Возвратить многомерный C-массив из функции в качестве результата стандартными средствами невозможно.

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

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

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

В первой статье я уже писал, что «Выделять память в одной функции, а освобождать в другой — плохая идея, чреватая ошибками». Поэтому рассматривайте этот пример только как демонстрацию работы с функциями и массивами указателей.

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

Массив указателей используется в каждой программе, которая может получать входную информацию из командной строки (или при её вызове от операционной системы). Одна из классических форм функции main() имеет вид:

Аргументами функции являются количество строк argc (размер массива указателей) и массив указателей на строки — argv . Т.е. argv — это массив указателей на массивы значений типа char .

Пожалуй это всё, что я хотел рассказать в этой статье. Надеюсь, что кто-то сочтёт её полезной для себя.

Стандартные библиотеки разработаны с учетом многолетнего опыта лучших программистов и они не больны "детскими болезнями крутизны в программизме".

С Вашего позволения выскажу несколько замечаний.

1.я бы использовал в конструкторе параметры по умолчанию. Естественно равные 1. тогда бы легко можно было задать вектор или скаляр
2. Для операций с матрицами использовал бы operator+() и пр. (В том числе и для умножения на число). Причем неплохо бы проверять размерности матриц.
3.Еще следует перегрузить функции ввода/вывода. (Вместо функции печати). Тогда бы вся демонстрация и проверка работы класса matrix заняла всего бы одну строчку:

Сомнительное использование входных параметров конструктора (т.е. размерности матрицы )в качестве индексов.
7. В задании говорится о коде возврата своего для каждой ошибки, как я понял. Поэтому в флаге здесь толку мало. Я бы ввел переменную типа int для хранения типа ошибки. Т.е. 0 - для успеха, 1 - нехватка памяти, 2 - несоответсвие размерности и т.д. Причем сделал бы ее разделяемой, т.е. одной для всех матриц, т.е. static, потому что она может характеризовать всю ситуацию, а не конкретную матрицу. (Нельзя же обвинять А, если она не совпадает по размерности с В ? )

Как вы считаете?

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

Вроде здесь уже нет смысла проверять указатель на пустоту,
так как само выделение памяти есть не правельное.

и в классе изначально нельзя указателю присвоить NULL(0) тоесть

Стандартные библиотеки разработаны с учетом многолетнего опыта лучших программистов и они не больны "детскими болезнями крутизны в программизме".

Вроде здесь уже нет смысла проверять указатель на пустоту,
так как само выделение памяти есть не правельное.

и в классе изначально нельзя указателю присвоить NULL(0) тоесть

согласен указатель на пустоту прверять не надо да и как бы ему присваевается новый адрес от оператора нью
а вот над переменными класа я бы задумался толи опечатка толи на самом деле ошибка *p двумерные массивы так не описывают
и с перегрузкой я согласен не сложно написать

ну я думаю перегрузка остальных оператороров не имеет смысла описывать приметив

Индексное выражение также может иметь несколько индексов, как показано ниже:

Индексные выражения связываются в направлении слева направо. Сначала вычисляется левое индексное выражение, expression1[expression2]. Адрес, получающийся в результате сложения expression1 и expression2, формирует выражение указателя. Затем к этому выражению указателя добавляется выражение expression3, чтобы образовать новое выражение указателя. Эти операции повторяются до тех пор, пока не будет добавлено последнее индексное выражение. После вычисления последнего индексного выражения выполняется оператор косвенного обращения (*), если конечное значение указателя не указывает на тип массива (см. примеры ниже).

Выражения с несколькими индексами ссылаются на элементы "многомерных массивов". Многомерный массив — это массив, элементы которого являются массивами. Например, первый элемент трехмерного массива является двумерным массивом.

Примеры

В следующих примерах массив с именем prop объявляется с тремя элементами, каждый из которых представляет собой массив 4x6, содержащий значения типа int .

Ссылка на массив prop выглядит следующим образом:

В приведенном выше примере показано, как ссылаться на второй отдельный элемент int массива prop . Массивы хранятся по строкам, поэтому последний индекс изменяется быстрее; выражение prop[0][0][2] ссылается на следующий (третий) элемент массива и т. д.

Этот оператор представляет собой более сложную ссылку на отдельный элемент массива prop . Выражение вычисляется следующим образом.

Первый индекс, 2 , умножается на размер массива int 4x6 и добавляется к значению указателя prop . Результат указывает на третий массив 4x6 массива prop .

Второй индекс, 1 , умножается на размер 6-элементного массива int и добавляется к адресу, представленному значением prop[2] .

Каждый элемент 6-элементного массива является значением типа int , поэтому конечный индекс, 3 , перед добавлением к выражению int умножается на размер prop[2][1] . Результирующий указатель указывает на четвертый элемент 6-элементного массива.

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

Ниже приведены два примера, в которых оператор косвенного обращения не применяется.

В первом из показанных операторов выражение prop[2][1] представляет собой допустимую ссылку на трехмерный массив prop ; эта ссылка относится к 6-элементному массиву (объявленному выше). Поскольку значение указателя указывает на массив, оператор косвенного обращения не применяется.

Аналогично результат выражения prop[2] во втором операторе ipp = prop[2]; представляет значение указателя, указывающее на двумерный массив.

Массив — это последовательность объектов того же типа, которые занимают непрерывную область памяти. Традиционные массивы в стиле C являются источником многих ошибок, но по-прежнему являются распространенными, особенно в старых базах кода. В современном C++ настоятельно рекомендуется использовать std::vector или std::array вместо массивов в стиле C, описанных в этом разделе. Оба этих стандартных типа библиотек хранят свои элементы в виде непрерывного блока памяти. Однако они обеспечивают гораздо большую безопасность типов и поддерживают итераторы, которые гарантированно указывают на допустимое расположение в последовательности. Дополнительные сведения см. в разделе "Контейнеры".

Объявления стека

В объявлении массива C++ размер массива указывается после имени переменной, а не после имени типа, как в некоторых других языках. В следующем примере для стека объявляется массив из 1000 двойников. Число элементов должно быть предоставлено в виде целочисленного литерала или в виде константного выражения. Это связано с тем, что компилятору необходимо знать, сколько пространства стека необходимо выделить; Он не может использовать значение, вычисленное во время выполнения. Каждому элементу в массиве присваивается значение по умолчанию 0. Если вы не назначаете значение по умолчанию, каждый элемент изначально содержит любые случайные значения, которые будут находиться в этом расположении памяти.

Первый элемент в массиве является нулевым элементом. Последним элементом является элемент (n-1), где n — количество элементов, которые может содержать массив. Число элементов в объявлении должно быть целочисленным типом и должно быть больше 0. Ваша ответственность заключается в том, чтобы программа никогда не передает значение оператору подстрока, который больше (size - 1) .

Массив нулевого размера является допустимым только в том случае, если массив является последним полем в struct или union если расширения Майкрософт включены ( /Za или /permissive- не заданы).

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

Объявления кучи

Может потребоваться слишком большой массив для выделения в стеке или размер которого неизвестлен во время компиляции. Этот массив можно выделить в куче с помощью new[] выражения. Оператор возвращает указатель на первый элемент. Оператор подстрока работает с переменной указателя так же, как и в массиве на основе стека. Можно также использовать арифметические указатели для перемещения указателя на любые произвольные элементы в массиве. Вы несете ответственность за обеспечение следующего:

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

В следующем примере показано, как определить массив в куче во время выполнения. В нем показано, как получить доступ к элементам массива с помощью оператора подстрока и с помощью арифметики указателя:

Инициализация массивов

Массив можно инициализировать в цикле, по одному элементу за раз или в одной инструкции. Содержимое следующих двух массивов идентично:

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

Когда массив передается функции, он передается в качестве указателя на первый элемент, будь то массив на основе стека или кучи. Указатель не содержит дополнительных сведений о размере или типе. Такое поведение называется разложением указателя. При передаче массива в функцию необходимо всегда указывать количество элементов в отдельном параметре. Это также означает, что элементы массива не копируются при передаче массива в функцию. Чтобы предотвратить изменение элементов функцией, укажите параметр в качестве указателя на const элементы.

В следующем примере показана функция, которая принимает массив и длину. Указатель указывает на исходный массив, а не копию. Так как параметр не const задан, функция может изменять элементы массива.

Объявите и определите параметр p массива так, const чтобы он был доступен только для чтения в блоке функции:

Эту же функцию также можно объявить таким образом, без каких-то изменений в поведении. Массив по-прежнему передается в качестве указателя на первый элемент:

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

Массивы, созданные из других массивов, являются многомерными. Такие многомерные массивы определяются путем последовательного размещения нескольких константных выражений, заключенных в квадратные скобки. Рассмотрим, например, следующее объявление:

Он задает массив типа int , концептуально упорядоченный в двумерной матрице из пяти строк и семи столбцов, как показано на следующем рисунке:


Концептуальная структура многомерного массива

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

В показанном выше объявлении определяется массив, состоящий из трех строк и четырех столбцов. Строки представляют фабрики, а столбцы — рынки, на которые фабрики поставляют свою продукцию. Значения — это стоимости транспортировки с фабрик на рынки. Первое измерение массива опущено, но компилятор заполняет его, проверяя инициализатор.

Использование оператора косвенного обращения (*) в типе n-мерного массива приводит к получению массива n-1. Если n равно 1, создается скаляр (или элемент массива).

Массивы C++ размещаются в памяти по срокам. Построчный порядок означает, что быстрее всего изменяется последний индекс.

Пример

Можно также опустить спецификацию границ для первого измерения многомерного массива в объявлениях функций, как показано ниже:

Эта функция FindMinToMkt записывается таким образом, что добавление новых фабрик не требует каких-либо изменений в коде, а просто перекомпиляции.

Инициализация массивов

Массивы объектов с конструктором класса инициализируются конструктором. Если в списке инициализатора меньше элементов, чем элементов в массиве, конструктор по умолчанию используется для остальных элементов. Если для класса не определен конструктор по умолчанию, список инициализаторов должен быть завершен, то есть для каждого элемента в массиве должен быть один инициализатор.

Рассмотрим класс Point , определяющий два конструктора:

Первый элемент aPoint создается с помощью конструктора Point( int, int ) , а оставшиеся два элемента — с помощью конструктора по умолчанию.

Статические массивы элементов (независимо от того, можно ли const ) инициализировать в их определениях (за пределами объявления класса). Пример:

Доступ к элементам массива

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

Если используются многомерные массивы, в выражениях можно использовать различные сочетания.

В приведенном выше коде multi представляет собой трехмерный массив типа double . Указатель p2multi указывает на массив типа double размера 3. В этом примере массив используется с одним, двумя и тремя индексами. Хотя обычно указываются все подстроки, как в инструкции cout , иногда бывает полезно выбрать определенное подмножество элементов массива, как показано в следующих cout инструкциях.

Перегруженный оператор подстрочного индекса

Как и другие операторы, оператор подстрока ( [] ) может быть переопределен пользователем. Поведение оператора индекса по умолчанию, если он не перегружен, — совмещать имя массива и индекс с помощью следующего метода.

Как и во всех дополнениях, включающих типы указателей, масштабирование выполняется автоматически для настройки размера типа. Результирующая величина не является n байтами из источника array_name ; вместо этого это n-й элемент массива. Дополнительные сведения об этом преобразовании см. в разделе "Аддитивные операторы".

Аналогично, для многомерных массивов адрес извлекается с использованием следующего метода.

((array_name) + (subscript1 * max2 * max3 * . * maxn) + (subscript2 * max3 * . * maxn) + . + subscriptn))

Массивы в выражениях

Если идентификатор типа массива отображается в выражении, отличном от sizeof адреса ( & ) или инициализации ссылки, он преобразуется в указатель на первый элемент массива. Пример:

Указатель psz указывает на первый элемент массива szError1 . Массивы, в отличие от указателей, не являются изменяемыми l-значениями. Вот почему следующее назначение является незаконным:

Читайте также: