Вот эта вещь.
Я могу полностью понять концепцию многомерного массива (давайте на некоторое время рассмотрим 2D), созданного указателем на массив с указателями и так далее …
Мы делаем что-то вроде этого:
// we can use dynamically defined size n and m
int n = 3, m = 5 ;
int **T = new int *[n];
for (int i = 0 ; i < n ; ++i)
T[i] = new int[m];
То, что мы получили, это:
(Проверь если я здесь)
Теперь проблема в том, когда мы делаем что-то вроде этого:
int n = 3, m = 5 ;
int T[n][m] ;
То, что у нас есть, это не то, что мы могли бы делать, что я показывал раньше.
Мы получаем что-то странное. Что такое Т? При печати T мы получаем то же значение, что и T [0].
Похоже, мы зарезервировали блок размером n * m без дополнительного массива указателей на строки.
У меня вопрос: запоминает ли компилятор размерность массива и количество строк и столбцов? И когда спрашивает T [i] [j], он фактически запрашивает * (T + i * n + j), так что этот n где-то хранится?
Проблема в том, что мы пытаемся передать эту вещь (T) в функцию. Я не знаю почему, но если n и m были константами, то можно передать T в качестве указателя на этот массив для работы, как в этой программе:
#include <stdio.h>
const int n = 3, m = 4 ; // this is constant!
void add_5(int K[][m])
{
for (int i = 0 ; i < n ; ++i)
for (int j = 0 ; j < m ; j++)
K[i][j] += 5 ;
}
int main()
{
// n x m array the most straight forward method
int T[n][m] ;
for (int i = 0 ; i < n ; ++i)
for (int j = 0 ; j < m ; ++j)
T[i][j] = i*m + j ;
for (int i = 0 ; i < n ; ++i)
{
for (int j = 0 ; j < m ; j++)
printf("%d ",T[i][j]) ;
printf("\n") ;
}
printf("\n") ;
// adding 5 to all cells
add_5(T) ;
printf("it worked!!\n") ;
for (int i = 0 ; i < n ; ++i)
{
for (int j = 0 ; j < m ; j++)
printf("%d ",T[i][j]) ;
printf("\n") ;
}
int ss ;
scanf("%d",&ss) ;
}
Но если n и m не постоянны, мы не можем.
Поэтому мне нужно передать динамически созданный указатель многомерного массива в функцию без выделения памяти для этого вручную.
Как это сделать?
в C ++ массив фактически является указателем, указывающим на блок памяти
Точно нет. Массив полностью отделен от указателя. Причина, по которой у вас может возникнуть эта путаница, заключается в стандартном преобразовании, называемом преобразованием массива в указатель. Рассматривать:
int arr[10];
Переменная arr
обозначает массив. Это не указатель вообще. Так уж сложилось, что во многих случаях имя массива будет преобразовано в указатель на его первый элемент. То есть преобразование превращает его в int*
,
int T[n][m] ;
В этом случае, T
это «массив n
массив m
int
s «. Вы упомянули, что печать как T
а также T[0]
дать тот же результат, и это связано с преобразованием массива в указатель.
Выражение T
может быть преобразован в указатель на первый элемент; то есть int (*)[m]
, потому что первый элемент T
сам по себе массив с m
элементы.
Выражение T[0]
может быть преобразован в указатель на первый элемент первого подмассива. Таким образом, вы получаете указатель на элемент T[0][0]
типа int*
,
Эти указатели содержат один и тот же адрес из-за способа размещения массива в памяти. Адрес, с которого начинается массив, совпадает с адресом первого элемента этого массива. Однако указатели не ведут себя одинаково. Если вы увеличиваете указатель в результате T
, вы двигаетесь к следующему подмассиву. Если вы увеличиваете указатель в результате T[0]
переходите к следующему int
,
Это может помочь вам взглянуть на диаграмму того, как двумерный массив расположен в памяти по сравнению с динамически размещенным «двумерным массивом». 2D-массив 3 на 3 будет выглядеть так:
0,0 0,1 0,2 1,0 1,1 1,2 2,0 2,1 2,2
┌─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┐
│ int │ int │ int │ int │ int │ int │ int │ int │ int │
└─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┘
Принимая во внимание, что если вы динамически распределяете трехмерный массив размером 3 на 3:
┌─────┐
│ │ // The int**
└──╂──┘
┃
▼
┌─────┬─────┬─────┐
│ │ │ ┿━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ // An array of int*
└──╂──┴──╂──┴─────┘ ┃
┃ ┗━━━━━━━━━━━━━━━┓ ┃
▼ ▼ ▼
┌─────┬─────┬─────┐ ┌─────┬─────┬─────┐ ┌─────┬─────┬─────┐
│ int │ int │ int │ │ int │ int │ int │ │ int │ int │ int │ // Arrays of ints
└─────┴─────┴─────┘ └─────┴─────┴─────┘ └─────┴─────┴─────┘
0,0 0,1 0,2 1,0 1,1 1,2 2,0 2,1 2,2
компилятор запоминает размерность массива и количество строк и столбцов?
Да, если у вас есть переменная с типом массива, размер массива является частью этого типа. Компилятор всегда знает тип переменной.
И когда спрашивает T [i] [j], он фактически запрашивает * (T + i * n + j), так что этот n где-то хранится?
Выражение T[i][j]
эквивалентно *(*(T + i) + j)
, Давайте поймем, что это делает. Во-первых, преобразование массива в указатель подвергается T
, давая int (*)[m]
, Затем мы добавим i
к этому, чтобы двигаться вперед, чтобы указать на i
подмассив Это тогда разыменовывается, чтобы получить subarray. Затем этот подмассив также подвергается преобразованию массива в указатель, давая int*
, Затем вы добавляете j
к этому, чтобы получить указатель на j
го int
объект в этом подмассиве. Это разыменовано, чтобы дать это int
,
Проблема в том, что мы пытаемся передать эту вещь (T) в функцию. Я не знаю почему, но если n и m были константами, то можно передать T в качестве указателя на этот массив для работы
Это на самом деле ложь. Вы не передаете 2D-массив в функцию. На самом деле, нет такой вещи как аргумент типа массива. Ваш аргумент int K[][m]
на самом деле эквивалентно int (*K)[m]
, То есть все аргументы типа массива преобразуются в указатели.
Поэтому, когда вы вызываете эту функцию с add_5(T)
вы не передаете массив, обозначенный T
, T
на самом деле проходит преобразование массива в указатель, чтобы дать вам int (*)[m]
а также этот указатель передается в функцию.
Вот пример 2d динамического массива:
const int Mapsize = n
int** GameField2 = 0;
GameField2 = new int*[Mapsize]; // memory alocation
for(int i = 0; i < Mapsize; i++)
GameField2[i] = new int[Mapsize];
for(int j = 0; j < Mapsize; j++)
for(int i = 0; i < Mapsize; i++)
GameField2[i][j] = 0;
если вы хотите работать с этим массивом в любой функции, просто передайте указатель следующим образом:
int Whatver(int** GameField)
{
GameField[a][b]=x;
}