Как ввести двумерный массив с клавиатуры в c
Обновлено: 21.11.2024
5 . 5 многомерных массивов
C/C++ поддерживает многомерные массивы, хотя их реализация не так эффективна, как в других языках (особенно в Fortran). На самом деле любой n -мерный массив на самом деле является одномерным массивом указателей, которые указывают на ( n - 1) -мерные массивы. и так далее рекурсивно. Подобно «обычным» массивам, многомерные символьные массивы несколько особенные, поэтому мы обсудим их отдельно. Конечно, в этой главе мы говорим только о статических массивах, т. е. выделенных локально (в стеке) и с размерами, известными во время компиляции.
5 . 5 . 1 Матрицы
Мы сосредоточим наше внимание на двумерных массивах (матрицах). Рассмотрим следующее определение: Мы объявили двумерный массив (матрицу) целых чисел с 2 строками и 4 столбцами (т. е. 8 элементов). Условно первый индекс соответствует строкам, а второй — столбцам (что мы называем строкой, а что столбцом — вопрос условности, элементы в любом случае располагаются в памяти линейно).
Вкладка матрицы инициализируется некоторыми начальными значениями. Инициализатор в правой части присваивания имеет форму массива с двумя элементами ( и ), каждый из которых сам является массивом. Важно помнить порядок, в котором будут храниться элементы: в C/C++ порядок следующий: строка за строкой (а не столбец за столбцом, как в Фортране): первая строка (с индексом 0) идет первой, за ней следует вторая строка. (с индексом 1) В инициализаторе нам пришлось заключать элементы каждой строки в фигурные скобки: здесь это было необходимо, потому что не все элементы матрицы были заданы. В первой строке задано только три значения: четвертой будет присвоено значение 0. Мы могли бы инициализировать все элементы без фигурных скобок, обозначающих строки. Однако это не стандартное обозначение, и его следует избегать. Кроме того, четкое указание последовательных строк отображает истинную природу матрицы как массива массивов.
На элементы массива можно ссылаться, указывая индексы строк и столбцов, каждый в отдельной паре квадратных скобок — tab[1][2] имеет значение 7 в нашем примере; нумерация начинается с 0.
Предположим, мы определили двумерный массив со строками dim1 и столбцами dim2. Мы знаем, что элементы хранятся построчно. Учитывая адрес первого элемента первой строки и индексы m и n , как вычислить адрес элемента tab[m][n] ? Этот элемент относится к ( m + 1) -й строке (с индексом m ), поэтому, чтобы добраться до его местоположения, мы должны перепрыгнуть через первые m строк, каждая из которых содержит dim2 элемента; поэтому мы должны «пропустить» m ⋅dim2 элементов. Затем нам нужно перепрыгнуть через первые n элементов строки, которой принадлежит tab[m][n], так как это ( n + 1) -й элемент этой строки. Отсюда следует, что всего мы должны пропустить
элементы. Здесь важен тот факт, что для вычисления этого сдвига нам не нужно знать, каково первое измерение нашей матрицы (т. е. нам не нужно знать dim1). В общем, чтобы можно было вычислить смещение элемента с заданными индексами (относительно начала массива), нужны все измерения, кроме первого. Рассмотрим пример:
Программа выводит: В основной функции мы определяем двумерный массив размером 3×4. Мы инициализируем некоторые его элементы; остальные будут инициализированы нулями.
Функция exchange меняет местами две строки с индексами, переданными в качестве двух последних аргументов функции. Нам не нужно разбираться во всех деталях функции; важна форма, в которой был объявлен его первый параметр: int[][4] . Информация о первом измерении функции не передается. Мы могли бы указать его, объявив int[3][4] , но это была бы просто документирующая информация для читателя кода: компилятор все равно ее полностью проигнорировал бы.
Однако необходима информация о втором измерении. В противном случае смещение элементов относительно начала массива не могло быть вычислено внутри функции.
Функция printArray печатает элементы переданного ей массива. Эта функция должна знать, сколько строк нужно напечатать, поэтому необходимо передать первое измерение массива (строка ➊). Обратите внимание, однако, что он передается отдельно через дополнительный аргумент, а не как элемент типа массива.
Проанализируем природу вкладки переменных с точки зрения соответствия массивов/указателей. Предположим, мы определяем следующее определение: Мы помним из раздела об арифметике указателей, что tab[i] эквивалентен *(tab+i) . Следовательно, tab[i][j] будет соответствовать *(tab[i]+j) и должен иметь целочисленное значение, поэтому tab[i]+j должен быть указателем на целое число. Поскольку j — это просто int, tab[i] должен быть указателем на целое число (указывающим на начало строки с индексом i).Но это всего лишь *(tab+i), и чтобы это был указатель, tab должен быть указателем на целое число, то есть что-то типа int** .
Есть еще одна проблема с нашими массивами: функция exchange в программе arr2dim.cpp меняет местами две строки с индексами, переданными в качестве аргументов. Однако он будет работать только для массивов с 4 столбцами из-за объявления типа его первого параметра. Если бы у нас была другая матрица, с другим количеством столбцов, нам пришлось бы писать другую функцию, хотя задача у нее была бы та же. Как бы мы могли написать более общую функцию, меняющую местами две заданные строки матрицы?
Одним из возможных способов является определение функции, которая принимает не матрицу, а явный одномерный массив указателей на строки, которые сами являются одномерными массивами целых чисел. Преимущество такого подхода в том, что при передаче массива нам не нужно указывать размер в его типе: тип будет просто указателем. Следовательно, получаем:
Первый параметр функции exchange имеет тип одномерный массив указателей на int s . Здесь нет информации о каких-либо размерах; размеры передаются в функцию отдельно и могут быть разными для разных вызовов, соответствующих матрицам разной формы.
Однако есть проблема: по заданной матрице tt (строка ➊ ) мы должны построить массив указателей, указывающих на строки — этого требует функция. Следовательно, мы должны определить дополнительный, вспомогательный массив указателей arr ( ➋ ). Его размер равен количеству строк, а его элементам присваиваются значения адресов строк tt , то есть tt[0] , tt[1] и tt[2] . Благодаря арифметике указателей внутри функции мы можем использовать arr как матрицу с двумя индексами (поскольку элементы tab имеют не тип int, а тип int* ). Результат показывает, что обе функции, exchange и printArray , работают правильно, хотя в их определении жестко не закодирована информация о размерах массивов.
5 . 5 . 2 массива C-строк
Чтобы привыкнуть ко всем этим сложностям, мы рассмотрим специальный, но важный пример (одномерного) массива C-строк, который можно рассматривать как двумерный массив символов.
Что такое t в строке ➊ ? Это в массиве (потому что за его именем следует открывающая квадратная скобка) указателей на const char s (потому что он следует за объявлением типа const char* ). Указатели из таблицы указывают на массивы символов (C-строки), инициализированные строками, заданными в буквальном виде в правой части присваивания (поэтому мы использовали const, а не «обычные» char s). Поскольку «массив элементов типа Type» соответствует типу Type*, наш t, будучи массивом элементов типа const char*, соответствует типу const char**. Это делает назначение из строки ➋ допустимым.
Далее мы печатаем значение v+2 . Это адрес из v, сдвинутый на две длины элементов v. Его тип — const char** , а не const char* — поэтому при печати мы получаем адрес как таковой, а не строку символов.
В следующих двух строках мы печатаем значение переменной, на которую указывает v+2 : тип этой переменной const char* , поэтому печатается строка (все символы, начинающиеся с адреса *(v+2) , что эквивалентно v[2] , первому встреченному символу NUL). Поскольку v[2] — это адрес третьей строки, мы увидим jklmno напечатанным.
В строках, начинающихся с ➌ , мы делаем то же самое на уровне отдельных символов. Выражение *(t+1) эквивалентно t[1] , которое является указателем на первый символ второй строки. Сдвинув его на две длины одного char , мы получим *(t+1)+2, который будет адресом третьего символа строки, на которую указывает *(t+1) ; разыменовывая его, мы получаем *(*(t+1)+2) что эквивалентно t[1][2] и это третий символ второй строки (буква 'g'). Мы можем проверить это, глядя на вывод: последние две строки показывают, что все эти операции могут быть выполнены над v (которой было присвоено значение t ), хотя он не был явно объявлен массивом: это просто указатель соответствующий тип.
Массивы строк широко используются во многих программах на C/C++, и очень важно понимать, что происходит «под капотом». Например, в виде такого массива в программу передаются аргументы командной строки — см. вводный раздел и там файл arguments.cpp программы.
Хотя массив аргументов командной строки объявлен как ' char* argv[] ', а не ' const char* argv[] ', мы никогда не должны пытаться модифицировать C-строки, на которые указывают элементы argv!
В заключение этого раздела давайте посмотрим, как читать C-строку из стандартного потока ввода (обычно подключенного к клавиатуре). Мы должны помнить, что все пробелы рассматриваются как разделители, а не как части строк. Рассмотрим пример:
Мы создаем массивы символов nap1 и nap2 с размерами, достаточными для хранения ожидаемых строк (кстати, это простой, но обычно не рекомендуемый способ чтения строк). Затем мы читаем данные в эти массивы, используя обычный механизм, предоставляемый cin. Как мы уже заметили в разделе о чтении входных данных, чтение одной части данных прекращается при встрече с пробелом, поэтому мы можем прочитать две строки, как показано ниже: Заметим, что заключение строки в апострофы не помогает: пробел обрабатывается как разделитель во всяком случае. Мы также можем видеть, что механизм, обеспечиваемый использованием объекта cin, гарантирует, что завершающий символ '\0' будет добавлен автоматически. Более эффективные способы чтения данных из входных потоков будут рассмотрены в главе об операциях ввода-вывода.
Синтаксис объявления двумерного массива не сильно отличается от одномерного массива. В двумерном массиве для объявления и доступа к элементам двумерного массива мы используем 2 индекса вместо 1.
Синтаксис: тип данных имя_массива[ROW][COL];
Общее количество элементов в двумерном массиве равно ROW*COL . Возьмем пример.
Этот массив может хранить 2*3=6 элементов. Вы можете визуализировать этот двумерный массив как матрицу из 2 строк и 3 столбцов.
Для доступа к отдельным элементам приведенного выше массива можно использовать два индекса вместо одного. Первый нижний индекс обозначает номер строки, а второй обозначает номер столбца. Как мы видим на изображении выше, и строки, и столбцы индексируются с 0 . Таким образом, первый элемент этого массива находится в arr[0][0], а последний элемент — в arr[1][2] . Вот как вы можете получить доступ ко всем остальным элементам:
arr[0][0] - относится к первому элементу
arr[0][1] - относится ко второму элементу
arr[0][2] - относится к третьему элементу
arr[1][0] - ссылка на четвертый элемент
arr[1][1] - ссылка на пятый элемент
arr[1][2] - ссылка на шестой элемент
Если вы попытаетесь получить доступ к элементу за пределами допустимых ROW и COL , компилятор C не отобразит никакого сообщения об ошибке, вместо этого будет напечатано мусорное значение. Обработка границ является обязанностью программиста.
arr[1][3] - будет напечатано мусорное значение, потому что последний допустимый индекс COL равен 2
arr[2][3] - будет напечатано мусорное значение, потому что последний допустимый индекс ROW и COL равен 1 и 2 соответственно
Как и в случае с одномерными массивами, мы можем использовать константы и символьные константы только для указания размера двумерного массива.
Для обработки элементов двумерного массива мы используем два вложенных цикла. Внешний цикл for проходит по всем строкам, а внутренний цикл for — по всем столбцам. Следующая программа все очистит.
Ожидаемый результат:
Как это работает:
В этой предыдущей программе нет ничего нового, заслуживающего объяснения. Мы просто используем два вложенных цикла for. Первый вложенный цикл for принимает данные от пользователя. А второй цикл for печатает элементы двумерного массива, как матрицу.
Инициализация двумерного массива аналогична одномерному массиву. Например:
После этой инициализации каждый элемент выглядит следующим образом:
Рассмотрите другую инициализацию.
Размер my_arr равен 4*3=12, но при инициализации мы указали только значение 8 элементов. В таких случаях оставшимся элементам будет присвоено значение 0 .
Отдельные элементы следующие:
В двумерных массивах указывать первое измерение необязательно, но всегда должно присутствовать второе измерение. Это работает только тогда, когда вы одновременно объявляете и инициализируете массив. Например:
Как обсуждалось ранее, двумерный массив можно визуализировать в виде матрицы. Следующая программа демонстрирует сложение двух матриц.
Ожидаемый результат:
Как это работает:
Две матрицы можно сложить или вычесть, только если они имеют одинаковую размерность. Другими словами, матрицу размера 2*3 можно добавить к другой матрице 2*3, но нельзя добавить или вычесть ее к матрице 2*4 или 3*2. Результирующий массив будет матрицей той же размерности, что и исходные два. Первые два цикла for просят пользователя ввести две матрицы. Третий цикл for добавляет соответствующие элементы mat1 и mat2 в новый массив mat3. Четвертый цикл for печатает элементы массива mat3 .
Можно даже создать массив из 3 и более измерений, но обычно в этом нет необходимости. Поэтому мы ограничимся только трехмерными массивами.
Вот как можно объявить трехмерный массив.
Трехмерный массив использует три индекса или индекса. Этот массив может хранить 2*3*2=12 элементов.
Вот как инициализировать трехмерный массив.
Вы можете представить этот массив как 2 двумерных массива, и каждый из этих двумерных массивов имеет 3 строки и 4 столбца;
Вот отдельные элементы массива:
Первая строка
Вторая строка
Многомерные массивы можно передавать в функции точно так же, как и одномерные массивы, но при этом необходимо указать размер всех остальных измерений, кроме первого. Например:
Если вам нужно передать arr[2][3] в функцию с именем func_1() , вам нужно объявить func_1() следующим образом:
сообщить об этом объявлении
Иногда мы часто используем списки строк символов, например, список имен учащихся в классе, список имен сотрудников организации, список мест и т. д. Список имен можно рассматривать как таблица строк. Чтобы сохранить весь список, мы используем двумерный массив строк на языке C.
Массив символов называется строкой. «Привет», «Привет» и т. д. являются примерами String. Точно так же массив строк представляет собой не что иное, как двумерный (2D) массив символов. Чтобы объявить массив строк в C, мы должны использовать тип данных char.
Примером двумерных символов или массива строк является
Объявление массива строк
Синтаксис:-
Здесь первый индекс (размер строки) указывает максимальное количество строк в массиве, а второй индекс (размер столбца) указывает максимальную длину каждой отдельной строки.
Например, char language[5][10] ; В массиве «язык» мы можем хранить не более 5 строк, и каждая строка может содержать не более 10 символов.
В языке C каждый символ занимает 1 байт памяти. Для массива «язык» будет выделено 50 байт (1*5*10) памяти. Где каждая строка будет иметь 10 байт (1*10) памяти.
Инициализация массива строк
Двумерные (2D) строки на языке C можно инициализировать напрямую, как показано ниже,
Двумерный (2D) массив строк в C также может быть инициализирован как,
Поскольку это двухмерный набор символов, поэтому каждая строка (одномерный массив символов) должна заканчиваться нулевым символом, т. е. ‘\0’
Второй способ объявления массива строк — это длительный процесс, и другие программисты не могут легко их прочитать по сравнению с предыдущим объявлением, поэтому в большинстве случаев мы предпочитаем первое объявление.
Каждая строка в этом массиве доступна по ее порядковому номеру. Индекс массива всегда начинается с 0.
Примечание 1: количество символов (размер столбца) должно быть объявлено во время инициализации двумерного массива строк.
Но следующие объявления недействительны.
Примечание 2. После инициализации массива строк мы не можем напрямую назначить новую строку.
Чтение и отображение двухмерного массива строк в C
Двумерный массив строк можно прочитать с помощью циклов. Для чтения мы можем использовать scanf(), gets(), fgets() или любые другие методы для чтения строки.
Двумерный массив строк можно отобразить с помощью циклов. Для отображения мы можем использовать printf(), puts(), fputs() или любые другие методы для отображения строки.
Пример программы для чтения двумерного массива символов или массива строк в C
Программа. Напишите программу для чтения и отображения двумерного массива строк на языке C.
Введите количество имен (
Подпишитесь на нас
сообщить об этом объявлении
Двумерный массив в C++ — это простейшая форма многомерного массива. Его можно представить как массив массивов. На изображении ниже показан двумерный массив.
2D-представление массива
Двумерный массив также называется матрицей. Это может быть любой тип, такой как целое число, символ, число с плавающей запятой и т. д., в зависимости от инициализации. В следующем разделе мы собираемся обсудить, как мы можем инициализировать двумерные массивы.
Инициализация двумерного массива в C++
Итак, как мы инициализируем двумерный массив в C++? Вот так просто:
Итак, как видите, мы инициализируем двумерный массив arr с 4 строками и 2 столбцами как массив массивов. Каждый элемент массива снова является массивом целых чисел.
Мы также можем инициализировать двумерный массив следующим образом.
В этом случае arr также представляет собой двумерный массив с 4 строками и 2 столбцами.
Печать 2D-массива в C++
Мы завершили инициализацию 2D-массива, теперь, не распечатывая его, мы не можем подтвердить, что это было сделано правильно.
Кроме того, во многих случаях нам может понадобиться распечатать результирующий 2D-массив после выполнения над ним некоторых операций. Так как же нам это сделать?
В приведенном ниже коде показано, как мы можем это сделать.
Вывод:
Печать 2D-массива
В приведенном выше коде
- Сначала мы инициализируем двумерный массив arr[4][2] определенными значениями
- После этого мы пытаемся напечатать соответствующий массив, используя два цикла for,
- внешний цикл for выполняет итерацию по строкам, а внутренний - по столбцам двумерного массива,
- Итак, для каждой итерации внешнего цикла i увеличивается и переводит нас к следующему одномерному массиву. Кроме того, внутренний цикл одновременно обходит весь массив 1D,
- И соответственно, мы печатаем отдельный элемент arr[ i ][ j ] .
Использование элементов 2D-массива в качестве пользовательского ввода
Ранее мы видели, как можно инициализировать двумерный массив заранее заданными значениями. Но мы также можем сделать его пользовательским вводом. Давайте посмотрим, как
Вывод:
Пользовательский ввод 2D-массива
Для приведенного выше кода мы объявляем двумерный массив 2X2 s . Используя два вложенных цикла for, мы проходим через каждый элемент массива и получаем соответствующие пользовательские данные. Таким образом, весь массив заполняется, и мы распечатываем его, чтобы увидеть результаты.
Сложение матриц с использованием двумерных массивов в C++
В качестве примера давайте посмотрим, как мы можем использовать двумерные массивы для выполнения сложения матриц и печати результата.
Вывод:
Сложение матриц с использованием двумерных массивов
- Возьмем две матрицы m1 и m2, содержащие не более 5 строк и 5 столбцов. И еще одна матрица m3, в которой мы будем хранить результат,
- В качестве пользовательского ввода мы использовали количество строк и столбцов для обеих матриц. Поскольку мы выполняем сложение матриц, количество строк и столбцов должно быть одинаковым для обеих матриц,
- После этого мы принимаем обе матрицы в качестве входных данных пользователя, снова используя вложенные циклы for,
- На данный момент у нас есть обе матрицы m1 и m2,
- затем мы проходим по матрице m3, используя два цикла for, и обновляем соответствующие элементы m3[i][j] значением m1[i][j]+m2[i][j] . Таким образом, к концу внешнего цикла for мы получим желаемую матрицу,
- Наконец, мы распечатываем результирующую матрицу m3.
Указатель на двумерный массив в C++
Если у нас может быть указатель на целое число, указатель на число с плавающей запятой, указатель на символ, то можем ли мы не иметь указателя на массив? Мы, конечно, можем. В следующей программе показано, как ее создать и использовать.
Вывод:
Указатель 2D-массива
- В приведенном выше коде мы пытаемся напечатать 2D-массив с помощью указателей.
- Как и раньше, сначала мы инициализируем двумерный массив s[5][2] . А также указатель (*p)[2] , где p — указатель, в котором хранится адрес массива из 2 элементов,
- Как мы уже говорили, двумерный массив можно разбить на массив массивов. Таким образом, в этом случае s на самом деле является массивом из 5 элементов, которые также являются массивами с 2 элементами для каждой строки.
- Мы используем цикл for для обхода этих 5 элементов массива, s. Для каждой итерации мы присваиваем p адрес s[i] ,
- Кроме того, внутренний цикл for выводит отдельные элементы массива s[i] с помощью указателя p. Здесь (*p + j) дает нам адрес отдельного элемента s[i][j], поэтому с помощью *(*p+j) мы можем получить доступ к соответствующему значению.
Передача двумерного массива в функцию
В этом разделе мы узнаем, как передать двумерный массив любой функции и получить доступ к соответствующим элементам. В приведенном ниже коде мы передаем массив a двум функциям show() и print(), которые распечатывают переданный 2D-массив.
Вывод:
Передача 2D-массива в функции
- В функции show() мы определили q как указатель на массив из 4 целых чисел посредством объявления int (*q)[4] ,
- q содержит базовый адрес нулевого одномерного массива
- Затем этот адрес присваивается q, указателю int, а затем с помощью этого указателя осуществляется доступ ко всем элементам нулевого одномерного массива.
- В следующий раз в цикле, когда i принимает значение 1, выражение q+i извлекает адрес первого одномерного массива. Это связано с тем, что q является указателем на нулевой одномерный массив, и добавление к нему 1 даст нам адрес следующего одномерного массива. Этот адрес снова присваивается q, и с его помощью осуществляется доступ ко всем элементам следующего одномерного массива
- Во второй функции print() объявление q выглядит следующим образом: int q[][4] ,
- Это то же самое, что и int (*q )[4], где q — указатель на массив из 4 целых чисел. Единственное преимущество состоит в том, что теперь мы можем использовать более знакомое выражение q[i][j] для доступа к элементам массива. Мы могли бы использовать то же выражение и в show(), но для лучшего понимания использования указателей мы используем указатели для доступа к каждому элементу.
Заключение
Итак, в этой статье мы обсудили двумерные массивы в C++, как мы можем выполнять различные операции, а также их применение в сложении матриц. Если у вас возникнут дополнительные вопросы, не стесняйтесь использовать комментарии.
Читайте также: