Подтвердить что ты не робот

Передача многомерного массива переменной длины в функцию

Есть много похожих вопросов, но все же я не смог найти ответа, соответствующего функции массивов переменной длины в C99/C11.

Как передать массив многомерной переменной длины в функцию в C99/C11?

Например:

void foo(int n, int arr[][]) // <-- error here, how to fix?
{
}

void bar(int n)
{
    int arr[n][n];
    foo(n, arr);
}

Компилятор (g++-4.7 -std=gnu++11) говорит:
error: declaration of ‘arr’ as multidimensional array must have bounds for all dimensions except the first

Если я изменю его на int *arr[], компилятор все еще жалуется:
error: cannot convert ‘int (*)[(((sizetype)(((ssizetype)n) + -1)) + 1)]’ to ‘int**’ for argument ‘2’ to ‘void foo(int, int**)’

Следующий вопрос, как передать его по значению и как передать его по ссылке? По-видимому, обычно вы не хотите, чтобы весь массив был скопирован, когда вы передаете его функции.

С массивами постоянной длины это просто, так как, как предполагает "константа", вы должны знать длину, когда объявляете функцию:

void foo2(int n, int arr[][10]) // <-- ok
{
}

void bar2()
{
    int arr[10][10];
    foo2(10, arr);
}

Я знаю, что передача массивов таким функциям не является лучшей практикой, и мне это совсем не нравится. Скорее всего, лучше делать с плоскими указателями или объектами (например, std: vector) или как-то иначе. Но все же, я немного любопытно, что здесь ответ с теоретической точки зрения.

4b9b3361

Ответ 1

Передача массивов в функции немного смешно в C и С++. Нет никаких значений типов массивов, поэтому вы фактически передаете указатель.

Чтобы адресовать 2D-массив (реальный, а не массив массивов), вам нужно передать 2 куска данных:

  • указатель туда, где он запускается
  • насколько широка одна строка

И это два отдельных значения, будь то C или С++ или с VLA или без или что-то еще.

Некоторые способы записать это:

Простейший, работает везде, но нуждается в более ручной работе.

void foo(int width, int* arr) {
    arr[x + y*width] = 5;
}

VLA, стандарт C99

void foo(int width, int arr[][width]) {
    arr[x][y] = 5;
}

VLA с реверсированными аргументами, объявление прямого параметра (расширение GNU C)

void foo(int width; int arr[][width], int width) {
    arr[x][y]=5;
}

С++ w/VLA (расширение GNU С++, ужасно уродливое)

void foo(int width, int* ptr) {
    typedef int arrtype[][width];
    arrtype& arr = *reinterpret_cast<arrtype*>(ptr);
    arr[x][y]=5;
}

Большое замечание:

Обозначение [x] [y] с 2D-массивом работает, потому что тип массива содержит ширину. Нет типов VLA = массивы должны быть исправлены во время компиляции.

Следовательно: если вы не можете использовать VLA, тогда...

  • нет способа обработать его в C,
  • Нет способа справиться с этим без прокси-класса с перегруженной операцией перегрузки в С++.

Если вы можете использовать расширения VLA (C99 или GNU С++), то...

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

Для С++ boost::multi_array является прочным выбором.

Обходной путь

Для 2D-массивов вы можете сделать два отдельных выделения:

  • 1D массив указателей на T (A)
  • 2D-массив T (B)

Затем установите указатели в (A) в соответствующие строки (B).

С помощью этой настройки вы можете просто передать (A) как простой T**, и он будет хорошо вести себя с индексированием [x][y].

Это решение хорошо подходит для 2D, но для более высоких размеров требуется больше и больше шаблонов. Это также медленнее, чем решение VLA из-за дополнительного слоя косвенности.

Вы также можете запустить аналогичное решение с отдельным распределением для каждой строки B. В C это выглядит как malloc-in-a-loop и аналогично векторам вектора С++. Однако это уберет преимущество наличия всего массива в одном блоке.

Ответ 2

Для этого нет четкого пути, но вы можете использовать обходной путь для обработки 2-мерного массива как одномерного массива, а затем переконвертировать его в двумерный массив внутри функции.

void foo2(int n, int *arr) 
{
    int *ptr; // use this as a marker to go to next block
    int i;
    int j;

    for(i = 0; i < n; i++)
    {
        ptr = arr + i*n; // this is the starting for arr[i] ...
        for (j = 0; j < n ;j++)
        {
            printf(" %d ", ptr[j]); // This is same as arr[i][j]
        }
    }
}

void bar2()
{
    int arr[10][10];
    foo2(10, (int *)arr);
}