2D-массив как аргумент функции

Почему вы не можете объявить аргумент 2D-массива в функции, как в обычном массиве?

void F(int bar[]){} //Ok void Fo(int bar[][]) //Not ok void Foo(int bar[][SIZE]) //Ok 

Зачем нужно объявлять размер столбца?

Вы не можете написать void Foo(int bar[][]) , потому что bar распадается на указатель. Представьте следующий код:

 void Foo(int bar[][]) // pseudocode { bar++; // compiler can't know by how much increase the pointer // as it doesn't know size of *bar } 

Таким образом, компилятор должен знать размер *bar , поэтому должен быть предоставлен размер самого правого массива.

Статические массивы:

Кажется, вы совершенно не поняли смысла. Я решил попытаться это объяснить. Как описано в некоторых из вышеприведенных ответов, 2D Array в C++ хранится в памяти как 1D Array .

 int arr[3][4] ; //consider numbers starting from zero are stored in it 

Выглядит примерно так в памяти.

 1000 //ignore this for some moments 1011 ^ ^ ^ ^ 0 1 2 3 4 5 6 7 8 9 10 11 |------------| |-----------| |-------------| First Array Second Array Third Array |----------------------------------------------| Larger 2D Array 

Учтите, что здесь Bigger 2D Array хранится как непрерывные единицы памяти. Он состоит из 12 элементов, от 0 до 11 . Строки равны 3 а столбцы – 4 . Если вы хотите получить доступ к третьему массиву, вам нужно пропустить все первый и второй массивы. То есть вам нужно пропустить элементы, равные количеству ваших cols умноженных на количество массивов, которые вы хотите пропустить. Выходит, что cols * 2 .

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

Итак, как он выполняет расчет? Скажем, он работает над column major order , то есть ему нужно знать количество столбцов для пропуска. Когда вы укажете один элемент этого массива как …

 arr[i][j] ; 

Компилятор автоматически выполняет этот расчет.

 Base Address + (i * cols + j) ; 

Попробуем формулу для одного индекса, чтобы проверить ее достоверность. Мы хотим получить доступ к 3rd элементу 2- 2nd массива. Мы сделаем это вот так …

 arr[1][2] ; //access third element of second array 

Мы выразились в формуле …

  1000 + ( 1 * 4 + 2 ) = 1000 + ( 6 ) = 1006 //destination address 

И мы достигаем по адресу 1006 где находится 6 . В двух словах нам нужно сообщить компилятору количество cols для этого вычисления. Поэтому мы отправляем его как параметр в функцию.

Если мы работаем над 3D Array , вроде этого …

 int arr[ROWS][COLS][HEIGHT] ; 

Мы должны были бы отправить ему последние два измерения массива в функцию.

 void myFunction (int arr[][COLS][HEIGHT]) ; 

Теперь формула станет такой.

 Base Address + ( (i * cols * height) + (j * height) + k ) ; 

Чтобы получить доступ к этому вот так …

 arr[i][j][k] ; 

COLS сообщает компилятору пропустить число 2D Array , а HEIGHT сообщает ему пропустить количество 1D Arrays . И так далее и т. Д. Для любого измерения.

Динамические массивы:

Как вы спрашиваете о поведении в случае динамических массивов, которые объявлены таким образом ..

 int ** arr ; 

Компилятор рассматривает их по-разному, потому что каждый индекс Dynamic 2D Array состоит из адреса в другой 1D Array . Они могут присутствовать или не присутствовать в смежных местах в куче. К их элементам обращаются их соответствующие указатели. Динамический аналог нашего статического массива выше выглядел бы примерно так.

 1000 //2D Pointer ^ ^ 2000 2001 2002 ^ ^ ^ ^ ^ ^ 0 4 8 1 5 9 2 6 10 3 7 11 1st ptr 2nd ptr 3rd ptr 

Предположим, что это ситуация. Здесь 2D Pointer или массив на месте 1000 . Он содержит адрес 2000 который сам содержит адрес ячейки памяти. Здесь арифметика указателя выполняется компилятором, в силу чего он судит о правильном расположении элемента.

Чтобы выделить память для 2D Pointer , мы делаем это.

 arr = new int *[3] ; 

И выделять память каждому указателю указателя, таким образом ..

 for (auto i = 0 ; i < 3 ; ++i) arr[i] = new int [4] ; 

В конце каждый ptr 2D Array сам по себе является массивом. Чтобы получить доступ к элементу, который вы делаете ...

 arr[i][j] ; 

Компилятор делает это ...

 *( *(arr + i) + j ) ; |---------| 1st step |------------------| 2nd step 

На первом шаге 2D Array разыменовывается на соответствующий 1D Array а на втором этапе 1D Array получает разыменованный доступ к соответствующему индексу. Именно по этой причине Dynamic 2D Arrays отправляются в функцию без упоминания их строки или столбца.

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

Потому что, когда вы передаете массив, он распадается на указатель, поэтому исключение внешнего измерения – это нормально, и это единственное измерение, которое вы можете исключить.

  void Foo(int bar[][SIZE]) 

эквивалентно:

  void Foo(int (*bar)[SIZE]) 

Компилятор должен знать, как долго второе измерение вычисляет смещения. 2D-массив фактически хранится в виде массива 1D. Если вы хотите отправить массив без известных измерений, подумайте о том, как использовать указатель на указатели и какой-то способ знать измерение самостоятельно.

Это отличается от, например, java, потому что в java тип данных также содержит измерение.

Поскольку статические 2D-массивы похожи на 1D-массивы с некоторым количеством сахара для лучшего доступа к данным, вам нужно подумать об арифметике указателей.
Когда компилятор пытается получить доступ к массиву элементов [x] [y], он должен вычислить адресную память элемента, то есть массив + x * NUM_COLS + y. Поэтому он должен знать длину строки (сколько элементов она содержит).
Если вам нужна дополнительная информация, я предлагаю эту ссылку .

существует в основном три способа выделения 2d-массива в C / C ++

выделять в кучу как 2d-массив

вы можете выделить 2d-массив в куче, используя malloc например:

 const int row = 5; const int col = 10; int **bar = (int**)malloc(row * sizeof(int*)); for (size_t i = 0; i < row; ++i) { bar[i] = (int*)malloc(col * sizeof(int)); } 

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

 void foo(int **baz) 

выделять в кучу как массив 1d

по различным причинам (оптимизация кеша, использование памяти и т. д.) может быть желательно сохранить массив 2d в виде массива 1d:

 const int row = 5; const int col = 10; int *bar = (int*)malloc(row * col * sizeof(int)); 

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

 bar[1 + 2 * col] // corresponds semantically to bar[2][1] 

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

 #define BAR(i,j) bar[(j) + (i) * col] .. BAR(2,1) // is actually bar[1 + 2 * col] 

вам нужно иметь подпись функции:

 void foo(int *baz) 

чтобы передать этот массив функции.

выделять стек

вы можете выделить 2d-массив в стеке, используя что-то вроде:

 int bar[5][10]; 

это выделяется как 1d-массив в стеке, поэтому компилятору необходимо знать второе измерение, чтобы достичь элемента, который вам нужен, как и во втором примере, поэтому также верно следующее:

 bar[2][1] == (*bar)[1 + 2 * 10] 

сигнатура функции для этого массива должна быть:

 void foo(int baz[][10]) 

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

дайте мне знать, если я где-то смешал строки и столбцы.

  • printf не печатать на консоли
  • Функция для динамического выделения матрицы
  • Как сериализовать / десериализовать словаря `из пользовательского XML, не использующего XElement?
  • Почему в OpenMP запрещен оператор! =?
  • C # webbrowser Ajax call
  • Куча выделяет 2D-массив (не массив указателей)
  • Не публичные пользователи для интерфейсов C #
  • Каковы новые возможности в C ++ 17?
  • Как усечь файл в C?
  • Как изменить имена свойств при сериализации с помощью Json.net?
  • Должен ли кто-нибудь установить указатели на «NULL» после освобождения?
  • Давайте будем гением компьютера.