Как назначить многомерный массив временной переменной? - PullRequest
6 голосов
/ 21 ноября 2011

Я хочу назначить статически распределенный многомерный массив временной переменной. Рассмотрим следующий пример:

void foo(int b[3][2])
{
    b[1][1] = 1; // no segmentation fault
}

int main()
{
    int a[3][2] = {{1, 2}, {11, 12}, {21, 22}};

    foo(a);

    int** c;
    c = (int**)&a;
    c[1][1] = 1; // segmentation fault on execution

    int* d[3];
    d[0] = (int*)&(a[0]);
    d[1] = (int*)&(a[1]);
    d[2] = (int*)&(a[2]);
    d[1][1] = 1; // no segmentation fault

    return 0;
}

В основном я хочу сделать то, что делает компилятор с параметром b из foo(). Но единственное рабочее решение, которое я мог придумать, это d. Есть ли не менее сложный путь?

Ответы [ 6 ]

12 голосов
/ 21 ноября 2011

cdecl ( справочная страница ) - ваш друг:

cdecl> explain int b[3][2]
declare b as array 3 of array 2 of int
cdecl> declare b as pointer to array 2 of int
int (*b)[2]

Итак, попробуйте это:

void foo(int b[3][2])
{
    b[1][1] = 1; // no segmentation fault
}

int main()
{
    int a[3][2] = {{1, 2}, {11, 12}, {21, 22}};

    foo(a);

    int (*b)[2] = a;

    b[1][1] = 1;

    return 0;
}
9 голосов
/ 21 ноября 2011

int[3][2] и int** несовместимые типы. Вы не можете кастовать друг друга.

Попробуйте это:

int (*c)[2];
c = a; //no need to cast 
c[1][1] = 1; //ok

Или вы можете сделать это (объявление, а также инициализация):

int (*c)[2] = a; //no need to cast 
c[1][1] = 1; //ok

Большой палец правила:

  • Не используйте приведение в стиле c в C ++. Используйте C ++ - стиль приведения. Если бы вы использовали приведение в стиле C ++, компилятор сказал бы вам проблему намного раньше ( ideone ) (не нужно запускать код, чтобы увидеть проблему):

    prog.cpp:5: error: invalid static_cast from type ‘int (*)[3][2]’ to type ‘int**’
    

    Но приведение в стиле C компилирует его нормально ( ideone ), как вы уже знаете.

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

6 голосов
/ 21 ноября 2011

Как вы, наверное, сейчас знаете, из других ответов тип a на самом деле не эквивалентен int** - он jsut распадается на (при возврате / передаче по значению).

int (*b)[2] = a; // would solve that

Существует более C ++ способ:

typedef std::array<std::array<int, 2>, 3> M23;

void foo(M23& b)
{
    b[1][1] = 1; 
}

int main()
{
    M23 a = {{1, 2}, {11, 12}, {21, 22}};

    foo(a);

    M23 d = a;
    d[1][1] = 1;
}
3 голосов
/ 21 ноября 2011

Если вы используете довольно современный компилятор, который поддерживает достаточно частей стандарта C ++ 11, вы можете использовать auto:

int a[3][2] = ...;
auto &b = a;
b[1][1] = 1;  // a[1][1] will be set

Конечно, и a, и bдолжен быть определен в той же области видимости, чтобы он работал.Например, в функции не может быть параметра auto (для этого нужны шаблоны).

2 голосов
/ 21 ноября 2011

Не приводите явно, поэтому попробуйте написать

 c = &a;

Затем компилятор GCC (используя gcc -Wall -g bidim.c -o bidim для компиляции) выдает правильное предупреждение:

bidim.c:13:7: warning: assignment from incompatible pointer type [enabled by default]

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

0 голосов
/ 21 ноября 2011

Первое, что приходит мне в голову, это использование typedef и ссылки вроде

typedef int thing_t[3][2];
thing_t& e = a;
e[1][1] = 1;

С указателями

int (*f)[2] = a;
f[1][1] = 1;

Другая возможность заключается в инкапсуляции в struct.

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