Не двойной ли [] [] эквивалентно ** double?
Я спрашиваю об этом, потому что моя программа имеет две функции для умножения матриц, они умножают только матрицы 4x4 и 4x1. Заголовки:
double** mult4x1(double **m1, double **m2);
double** mult4x4(double **m1, double **m2);
Они делают m1 * m2 и возвращают его в ** double, ниже - фрагмент умножения 4x4.
double** mult4x4(double **m1, double **m2){
double** result = (double**) malloc(sizeof(double)*4);
for (int i = 0; i < 4; i++) {
result[i] = (double*) malloc(sizeof(double)*4);
}
...multiply...
return result;
}
Разница между mult4x1 и mult4x4 заключается только в индексах, используемых внутри них.
У меня эти 3 матрицы:
double m1[4][4] = {
{2, 3, 5, 6},
{9, 8, 1, 7},
{5, 4, 3, 1},
{7, 6, 1, 2}
};
double m2[4][4] = {
{1, 0, 0, 0},
{0, 1, 0, 0},
{0, 0, 1, 0},
{0, 0, 0, 1}
};
double m3[4][1] = {
{2},
{3},
{3},
{1}
};
Пытаясь умножить эти матрицы, возникает ошибка.
double** test = mult4x4(m1, m2);
double** test2 = mult4x1(identity4x4(), m3);
//identity4x4() creates a 4x4 identity matrix - double** identity4x4();
Урожайность:
Ошибка: невозможно преобразовать double (*)[4]' to
double * 'для аргумента 1' to
double * mult4x4 (double *, double *)'
Ошибка: невозможно преобразовать double (*)[1]' to
double * 'для аргумента 2' to
double * mult4x1 (double *, double *)'
Не двойной [] [] должен быть равен ** double? Массив массивов двойных.
Любые разъяснения, неправильные представления и ошибки приветствуются.
Ответы
Ответ 1
Нет.
A double**
является указателем на указатель на double (double*
).
Поэтому на самом деле он должен быть создан следующим образом (обратите внимание на дополнительный * в первом файле malloc sizeof()):
double** result = (double**) malloc(sizeof(double*)*4);
for (int i = 0; i < 4; i++) {
result[i] = (double*) malloc(sizeof(double)*4);
}
Итак, в памяти это будет выглядеть так:
[] -> { d,d,d,d }
[] -> { d,d,d,d }
[] -> { d,d,d,d }
[] -> { d,d,d,d }
Есть 4 буфера, которые содержат 4 двойника, но не являются непрерывными.
Хотя ваш двойной [4] [4] является непрерывным буфером в памяти, например:
{ { d,d,d,d } { d,d,d,d } {d,d,d,d} {d,d,d,d} }
Ответ 2
Хотя оба double[N][M]
и double**
позволяют вам работать с 2D-массивами, они, безусловно, не эквивалентны: первый представляет собой 2D-массив из double
s, а позже представляет собой указатель на указатель на double, который может быть интерпретирован (с помощью удобного синтаксиса квадратной скобки C/С++) в виде массива указателей на double
или как массив массивов double
.
double[N][M]
представляет собой 2D-массивы "прямоугольной" формы, а double**
позволяет создавать массивы "свободной формы" (зубчатые), выделяя разные объемы памяти каждой строке вашей матрицы.
Разница с компилятором заключается в том, что данный double[N][M]
и пара целых чисел {r,c}
он может вычислять местоположение элемента, вычисляя смещение от начала массива, не читая ничего из памяти. Однако при использовании double**
компилятор должен вычислить адрес указателя на строку, прочитать этот указатель и только затем вычислить адрес целевого элемента. Из-за этой разницы эти два не являются взаимозаменяемыми.
Ответ 3
Нет типа [][]
. На самом деле у вас есть m2
, который представляет собой массив массивов размером 4 типа double и m1
, который представляет собой массив массивов размером 1. Массив массивов размера 4 не эквивалентен двойному указателю.
Ответ 4
Ну, надеюсь, я не буду здесь глупым, но нотация double [][]
также используется, когда вы обращаетесь к непрерывному блоку памяти, а double**
не обязательно непрерывна.
Я думаю, что это причина ошибки. Даже если вы можете использовать одну и ту же семантику для доступа к значениям, они на самом деле разные типы.
Ответ 5
[] [] не эквивалентен **;
double ** var;
- указатель указателей, и, как вы можете видеть из вашего выделения в памяти, вы держите массив указателей, а каждый указатель внутри указывает на массив значений double
double var [4][4];
хранится в памяти на месте и несколько эквивалентен double *; В этом случае знает размер матрицы (4 x 4), поэтому, например, при использовании var[2][2]
, он знает, где находится то, что находится в памяти; var[x][y]
переводится примерно так: var[x + y * 4]
и декларация может быть интерпретирована как double var [16];
Raxvan.
Ответ 6
Нет, это не так. double[n][m]
выделяет один блок памяти, достаточно большой для вашего двухмерного массива. Затем вы получаете возможность не вычислять собственную индексацию. При передаче двумерного массива в функцию компилятор требует от вас указать размер внутреннего измерения, чтобы он мог обработать индексирование.
double**
является указателем на указатель. Так как C допускает арифметику указателей, это может означать массив указателей, где каждый указатель указывает на массив двойников. Значения сами по себе не должны находиться в непрерывном блоке памяти. Как вы можете видеть, они совсем другие звери.
Если у вас есть double[n][m]
и вы хотите передать его в double **
, вам нужно будет создать свой собственный индексный массив.
Ответ 7
Нет... m1
- это массив с четырьмя элементами, каждый из которых представляет собой массив из четырех элементов. То же самое с m2
. Даже если первый "уровень" распадается из массива в указатель, второй "уровень" не будет. Вопрос в том, почему? Давайте посмотрим на некоторый код:
/* double[4][4] implicitly converts to double(*)[4]
* giving you a pointer to first element of m1 so
* you get back an array of four doubles.
*/
double (*pm1a)[4] = m1[0];
/* This isn't right, but let pretend that it was
* and that what you got back was a pointer
* to a pointer that pointed to m1 properly.
*/
double **pm1b = (double **)m1[0];
Итак, что произойдет с нашим гипотетическим кодом, если мы будем делать pm1a[1]
и pm1b[1]
?
pm1a[1]
отлично: он будет правильно продвигать pm1a
на 4 * sizeof(double)
(так как указатель указывает на double[4]
).
pm1b[1]
, с другой стороны, сломается: он будет продвигаться на неправильную сумму: размер указателя на double.
Но это еще не все. Там еще более тонкая ошибка. Если оба измерения должны были распадаться, компилятор не мог знать, что к массиву обращаются. Он будет счастливо интерпретировать pm1b[1]
как указатель на двойник. И что тогда происходит? Это займет какое-либо значение double
, сохраненное в этом месте, и обработает его как указатель на double
.
Вы можете понять, почему это было бы катастрофой.
Ответ 8
Другое отличие состоит в том, что вы не можете сделать double, чтобы указать на любой другой адрес памяти, если вы объявите его как double [] [] (что-то вроде финального поля)
Но если вы объявите его как ** double, тогда он может указывать на любую ячейку памяти.
Ответ 9
Нет, они определенно не эквивалентны, первый представляет двумерный массив удвоений, а последний представляет собой указатель на указатель на double.