2009-08-16 3 views
2

У меня есть функция, которая принимает указатель на указатель как аргумент.2D-массивы с C++

func(double **arr, int i); 

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

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

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

func (&arr); 

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

ответ

10

А 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] | ... 
          +---------+---------+ 

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


Для более полного обсуждения см my answer к previous questions (который на самом деле обратиться с, но проблемы те же).

+0

Итак, как мне передать массив моей функции? Я запутался. Я хочу передать его func без изменения его декларации. – zack

16

Тип arr является double[X][Y] - то есть, массив X массивов Y удваивается - где X и Y зависит от ваших инициализаторах. Это не так же, как тип указателя. Однако, согласно правилам преобразования C, массив может распадаться на указатель на его элемент. В вашем случае тип, возникающий в результате такого распада, будет двойным (*) [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); 
+0

Красивый и всеобъемлющий. Но так что, если вы не можете изменить fn? Просто оберните его, чтобы массив массивов верхнего уровня был стек-локальным (либо стековый разряд, либо исключающая кучу куча). То есть, сам массив 2d содержит смежные массивы 1d, и вы можете просто указать на них. –

+0

В качестве альтернативы вы можете использовать хороший синтаксис инициализатора для группы отдельных 1d-массивов и указать на них. Синтаксис инициализатора массива доступен только в области верхнего уровня, не так ли? –

+0

Хорошо, я отредактирую. Инициализатор массива доступен в любой области (включая локальную/автоматическую), но не в 'new'. –

-1
func(double *arr[100]) 

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

arr[y][x] 

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

arr[100*x+y] 

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