2D-массивы с C++

У меня есть функция, которая принимает указатель на указатель в качестве аргумента.

func(double **arr, int i);

где в основной функции массив определяется следующим образом:

double arr[][] = //some initialization here;

Как я могу вызвать эту функцию из моего основного кода. Я пробовал следующее, но это дает ошибку

func (&arr);

Не работает. Любая помощь приветствуется. Спасибо


person zack    schedule 16.08.2009    source источник


Ответы (3)


double **p — это не то же самое, что double[][] a, поэтому вы не можете передать одно за другое.

В частности, переменная двумерного массива представляет собой (один!) блок памяти, содержащий двойные значения, к которым вы можете получить доступ с помощью синтаксиса [][]. Для этого требуется, чтобы компилятор знал размерность массива, чтобы он мог вычислить правильное смещение для каждого элемента. Он также может прозрачно распадаться на указатель на этот блок памяти, но при этом теряется понимание того, как получить доступ к этой памяти как к двумерному массиву: он фактически становится указателем на удвоение.

+----+           +---------+---------+---------+
| (a---------->) | a[0][0] | a[0][1] | a[0][2] | ...
+----+           +---------+---------+---------+ 
                 | a[1][0] | a[1][2] | ...
                 +---------+---------+
                   ...

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

+---+       +------+      +---------+---------+
| p-------->| p[0]------->| p[0][0] | p[0][3] | ...
+---+       +------+      +---------+---------+
            | p[1]--\    
            +------+ \    +---------+---------+
             ...      --->| p[1][0] | p[1][4] | ...
                          +---------+---------+

Хотя синтаксис выглядит одинаково, эти две структуры имеют совершенно разную семантику.


Для более полного обсуждения см. >мой ответ на предыдущий вопросы (которые на самом деле относятся к c, но вопросы те же).

person dmckee --- ex-moderator kitten    schedule 16.08.2009
comment
Итак, как мне передать массив в мою функцию? Я смущен. Я хочу передать его в func без изменения объявления. - person zack; 17.08.2009

Тип arr - это double[X][Y], то есть массив X массивов Y удваивается, где X и Y зависят от ваших инициализаторов. Это не то же самое, что тип указателя. Однако, согласно правилам преобразования C, массив может распадаться на указатель на его элемент. В вашем случае тип, полученный в результате такого распада, будет double(*)[Y] - указатель на массив двойников Y. Обратите внимание, что это указатель на массив, а не массив указателей, поэтому он больше не будет разрушаться. На этом этапе вы получаете несоответствие типов, так как ваша функция ожидает double**.

Правильный способ справиться с этим - рассматривать массив как одномерный и передавать ширину. Так:

void func(double* arr, int w) {
   // arr[2][3]
   arr[2*w + 3] = ...;
}


double x[6][8] = { ... };
func(&x[0][0], 8);

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

template <int W, int H>
inline void func(const double (&arr)[W][H]) {
  arr[2][3] = ...;
}

double x[6][8] = { ... };
func(x); // W and H are deduced automatically

Однако это не сработает, когда все, что у вас есть, это указатель (например, когда массив new-аллоцирован, а его размер вычисляется во время выполнения). В наиболее общем случае вместо этого следует использовать контейнеры C++. Только со стандартной библиотекой обычно используется вектор векторов:

#include <vector>

void func(std::vector<std::vector<double> > arr) {
  arr[2][3] = ...;
}

std::vector<std::vector<double> > x(6, std::vector<double>(8));
x[0][0] = ...;
...
func(x);

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

void func(boost::multi_array<double, 2> arr) { // 2 means "2-dimensional"
  arr[2][3] = ...;
}

boost::multi_array<double, 2> x(boost::extents[6][8]);
x[0][0] = ...;
...
func(x);

[EDIT] вы говорите, что не можете изменить определение своей функции. Если это так, то это действительно функция, обрабатывающая свой аргумент как массив указателей, поэтому вам следует просто соответствующим образом выделить свою структуру данных. Например:

double x1[8] = { 1, 2, ... };
double x2[8] = { 3, 4, ... };
...
double* x[6] = { x1, x2, ... };

func(x);
person Pavel Minaev    schedule 16.08.2009
comment
Красиво и всеобъемлюще. Но что с того, если вы не можете изменить fn? Просто оберните его так, чтобы массив массивов верхнего уровня был локальным для стека (либо выделение стека, либо куча, безопасная для исключений). То есть сам массив 2d содержит непрерывные массивы 1d, и на них можно просто указать. - person Jonathan Graehl; 17.08.2009
comment
В качестве альтернативы вы можете использовать хороший синтаксис инициализатора для группы отдельных массивов 1d и указывать на них. Синтаксис инициализатора массива доступен только в области верхнего уровня, верно? - person Jonathan Graehl; 17.08.2009
comment
Хороший вопрос, я отредактирую. Инициализатор массива доступен в любой области видимости (включая локальную/авто), но не в new. - person Pavel Minaev; 17.08.2009

func(double *arr[100])

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

  arr[y][x]

превращается в

  arr[100*x+y]

Если вы определяете свою функцию как func(double **a), то вы указываете указатель на массив указателей, как указал dmckee

person shimpossible    schedule 16.08.2009