Перейти к содержимому

Как инициализировать двумерный массив c

  • автор:

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

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

спецификатор типа имя_массива [размерN] . [размер1] = ;

Список значений — это разделенный запятыми список констант, совместимых по типу со спецификатором типа. Первая константа помещается в первый элемент массива, вторая — во второй и так далее. За последней константой списка нет запятой. Обратим внимание, что точка с запятой следует за >. В следующем примере 10-элементный целочисленный массив инициализируется числами от 1 до 10:

Это означает, что i[0] получит значение 1, i[9] — 10.

Массивы символов, содержащие строки, допускают удобную инициализацию в виде

char имя_массива [размер] = «строка»;

При данной инициализации нулевой терминатор автоматически добавляется к концу строки. Нижеприведенный фрагмент кода инициализирует строку str фразой «hello»:

char str[6] = «hello»;

Это можно также записать:

Обратим внимание, что в данной версии следует явным образом указать нулевой символ. Поскольку все строки в С оканчиваются нулевым символом, следует убедиться, что массив достаточно длинный для его вмещения. Именно поэтому str состоит из 6 символов, хотя «hello» имеет только 5 символов.

Многомерные массивы инициализируются так же, как и одномерные. Следующий пример инициализирует sqrs числами от 1 до 10 и их квадратами:

Здесь sqrs[0][0] содержит 1, sqrs[0][1] содержит 1, sqrs[1][0] содержит 2, sqrs[1][1] содержит 4 и так далее.

При инициализации многомерных массивов можно добавить фигурные скобки вокруг каждого измерения. Это так называемая субагрегатная группировка. Ниже приведен еще один способ записи предыдущего объявления:

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

Как инициализировать двумерный массив в классе? [закрыт]

Вопросы с просьбами помочь с отладкой («почему этот код не работает?») должны включать желаемое поведение, конкретную проблему или ошибку и минимальный код для её воспроизведения прямо в вопросе. Вопросы без явного описания проблемы бесполезны для остальных посетителей. См. Как создать минимальный, самодостаточный и воспроизводимый пример.

Закрыт 6 лет назад .

#include #include using namespace std; const int n = 8; int dx[] = ; int dy[] = ; class HorseMove < public: int **Solution = new int *[n]; for (int i = 0; i < n; i++)< Solution[i] = new int *[n]; >bool Success = false; int Step(int i, int x, int y) < if (Success) < return 1; >if ((x < 0) || (x >= n) || (y < 0) || (y >= n) || (Solution[x][y] > 0)) < return 1; >Solution[x][y] = i; if (i = n * n) < Success = true; >else < for(int k = 0; k > if (!Success) < Solution[x][y] = 0; >> >; const int x0 = 1, y0 = 3; int main(void) < setlocale(LC_ALL, "Russian"); HorseMove Chess; Chess.Step(1, x0, y0); if (Chess.Success) < cout return 0; > 

Отслеживать
219k 15 15 золотых знаков 119 119 серебряных знаков 230 230 бронзовых знаков
задан 23 апр 2017 в 13:32

Это у вас точно такой код? Сразу после public: не объявление члена, а голый код. И — все же поясните, что вы хотите сделать, из вашего кода непонятно, где именно проблемы.

23 апр 2017 в 13:34

Дружище, прочти какую-нибудь хорошую книгу для начинающих, а то твой код очень уж странный. Начни с того, что не используй C-style двумерные массивы, сырые указатели, глобальные переменные и константы, не пиши названия переменных с больших букв (ни в CamelCase, ни в stl_case так не делается), приучи себя по возможности не использовать using namespace std , а везде приписывать std:: при указании функций, например ( std::cout ), в будущем тебе это пригодится. И да, почитай, что такое классы и зачем они нужны.

Многомерные массивы в C++ — практическое пособие

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

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

  • Классика жанра
    • Определение автоматических многомерных массивов
    • Инициализация
    • Заполнение массива значениями
    • Вывод значений массива на консоль
    • Расположение в памяти
    • Хаки
      • №1
      • №2
      • Создание и уничтожение динамических многомерных массивов
      • Где собака порылась
      • Ещё раз о предосторожности
      • Передача в функцию многомерного C-массива
      • Передача в функцию многомерного динамического массива

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

       #include using namespace std; const unsigned int DIM1 = 3; const unsigned int DIM2 = 5; int ary[DIM1][DIM2]; int main() < for (int i = 0; i < DIM1; i++) < for (int j = 0; j < DIM2; j++) < ary[i][j] = (i + 1) * 10 + (j + 1); >> // . 

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

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

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

       cout return 0; > 

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

       11 12 13 14 15 21 22 23 24 25 31 32 33 34 35 

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

       #include using namespace std; const unsigned int DIM1 = 3; const unsigned int DIM2 = 5; const unsigned int DIM3 = 2; int ary[DIM1][DIM2][DIM3]; int main() < for (int i = 0; i < DIM1; i++) < for (int j = 0; j < DIM2; j++) < for (int k = 0; k < DIM3; k++) < ary[i][j][k] = (i + 1) * 100 + (j + 1) * 10 + (k + 1); cout cout cout return 0; > 

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

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

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

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

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

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

       cout cout return 0; // точка останова здесь 

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

      0x00A9F218 01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00 . 0x00A9F228 05 00 00 00 06 00 00 00 07 00 00 00 08 00 00 00 . 0x00A9F238 09 00 00 00 0a 00 00 00 0b 00 00 00 0c 00 00 00 . 0x00A9F248 0d 00 00 00 0e 00 00 00 0f 00 00 00 10 00 00 00 . 0x00A9F258 11 00 00 00 12 00 00 00 13 00 00 00 14 00 00 00 . 0x00A9F268 15 00 00 00 16 00 00 00 17 00 00 00 18 00 00 00 . 0x00A9F278 19 00 00 00 1a 00 00 00 1b 00 00 00 1c 00 00 00 . 0x00A9F288 1d 00 00 00 1e 00 00 00 00 00 00 00 00 00 00 00 . 

      Хаки

      №1

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

       > int *ptr = (int *)ary; // так не надо делать ;-) for (int i = 0; i < DIM1; i++) < for (int j = 0; j < DIM2; j++) < cout cout return 0; > 
       cout

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

      №2

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

       > for (int i = 0; i < DIM1; i++) < for (int j = 0; j < DIM2; j++) < cout cout return 0; > 

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

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

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

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

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

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

       // работа с массивом for (int i = 0; i < DIM1; i++) < for (int j = 0; j < DIM2; j++) < ary[i][j] = (i + 1) * 10 + (j + 1); >> for (int i = 0; i < DIM1; i++) < for (int j = 0; j < DIM2; j++) < cout cout // уничтожение for (int i = 0; i < DIM1; i++) < delete [] ary[i]; >delete [] ary; return 0; > 

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

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

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

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

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

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

       > // работа с массивом for (int i = 0; i < DIM1; i++) < for (int j = 0; j < DIM2; j++) < for (int k = 0; k < DIM3; k++) < ary[i][j][k] = cnt++; cout cout cout // уничтожение for (int i = 0; i < DIM1; i++) < for (int j = 0; j < DIM2; j++) < delete [] ary[i][j]; >delete [] ary[i]; > delete [] ary; return 0; > 

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

      Работа с динамическим многомерным массивом синтаксически полностью совпадает с работой с многомерным 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) void func(int ary[][DIM2]) < . >// (2) 

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

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

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

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

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

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

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

       #include using namespace std; const unsigned int DIM1 = 3; const unsigned int DIM2 = 5; int **array_generator(unsigned int dim1, unsigned int dim2) < int **ptrary = new int * [dim1]; for (int i = 0; i < dim1; i++) < ptrary[i] = new int [dim2]; >return ptrary; > void array_destroyer(int **ary, unsigned int dim1) < for (int i = 0; i < dim1; i++) < delete [] ary[i]; >delete [] ary; > int main() < int **matrix; // создание массива matrix = array_generator(DIM1, DIM2); // использование for (int i = 0; i < DIM1; i++) < for (int j = 0; j < DIM2; j++) < matrix[i][j] = (i + 1) * 10 + (j + 1); cout > // уничтожение array_destroyer(matrix, DIM1); return 0; > 

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

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

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

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

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

      Да пребудет с вами святой Бьярн и апостолы его! 😉

      Автор статьи: Cranium aka Череп.

      Как инициализировать двумерный массив c

      Каждый массив имеет такую характеристику как размерность. Количество размерностей соотвествует числу пар квадратных скобок. Например:

      int numbers[3];

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

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

      int numbers[3][2];

      Здесь массив numbers имеет две размерности (две пары квадратных скобок): первая размерность равна 3, а вторая размерность — 2. Такой массив состоит из трех элементов, при этом каждый элемент представляет массив из двух элементов. Двухмерный массив еще можно представить в виде таблицы, где первая размерность представляет количество строк, а вторая размерность — количество столбцов.

      Подобным образом можно определять массивы и с большим количеством размерностей, например, трехмерный массив:

      int numbers[3][2][2];

      Как и в общем случае многомерный массив можно инициализировать некоторыми значениями, например, нулями:

      int numbers[3][2] <>;

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

      int numbers[3][2] < , // первая строка , // вторая строка // третья строка >;

      Вложенные фигурные скобки очерчивают элементы для каждого подмассива. Такой массив еще можно представить в виде таблицы:

      Возможна также инициализация не всех элементов, а только некоторых:

      int numbers[3][2] < , <>, >;

      В этом случае значения присваиваются первым элементам массивов, а остальные элементы инициализируются нулями.

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

      int numbers[][2] < , , >;
      Обращение к элементам

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

      #include int main() < int nums[3][2] < , , >; // получаем значение элемента int n = nums[1][0]; // вторая строка, первый столбец std::cout  nums[0][0]nums[0][1] nums[0]12 nums[1][0]nums[1][1] nums[1]34 nums[2][0]nums[2][1] nums[2]56  

      Соответственно выражение nums[1][0] представляет обращение к первому элементу второго подмассива (первый столбец второй строки)

      Перебор многомерного массива

      Переберем двухмерный массив:

      #include int main() < const int rows = 3, columns = 2; int numbers[rows][columns] < , , >; for(int i=0; i < rows; i++) < for(int j=0; j < columns; j++) < std::cout std::cout >

      Также для перебора элементов многомерного массива можно использовать другую форму цикла for, которая специально предназначена для перебора последовательностей:

      #include int main() < const int rows = 3, columns = 2; int numbers[rows][columns] < , , >; for(auto &subnumbers : numbers) < for(int number : subnumbers) < std::cout std::cout >

      Для перебора массивов, которые входят в массив, применяются ссылки. То есть во внешнем цикле for(auto &subnumbers : numbers) &subnumbers представляет ссылку на подмассив в массиве. Во внутреннем цикле for(int number : subnumbers) из каждого подмассива в subnumbers получаем отдельные его элементы в переменную number и выводим ее значение на консоль.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *