Как передать многомерный массив функции в C и C++

#include<stdio.h>
void print(int *arr[], int s1, int s2) {
    int i, j;
    for(i = 0; i<s1; i++)
        for(j = 0; j<s2; j++)
            printf("%d, ", *((arr+i)+j));
}

int main() {
    int a[4][4] = {{0}};
    print(a,4,4);
}

Это работает в C, но не в C++.

ошибка:

cannot convert `int (*)[4]' to `int**' for argument `1' to 
`void print(int**, int, int)'

Почему это не работает в C++? Какое изменение необходимо, чтобы быть сделанным?

42
задан Robert Gamble 13 May 2010 в 20:43
поделиться

9 ответов

#include<cstdio>
template <size_t N, size_t M>
struct DataHolder
{
    int data[N][M];
    DataHolder()
    {
       for(int i=0; i<N; ++i)
           for(int j=0; j<M; ++j)
               data[i][j] = 0;
    }
};

template <size_t N, size_t M>
void print(const DataHolder<N,M>& dataHolder) {
    printf("\n");
    for(int i = 0; i<N; i++) {
        for(int j = 0; j<M; j++) {
            printf("%d, ", dataHolder.data[i][j]);
        }
    }
    printf("\n");
}

int main() {
    DataHolder<4,4> a;
    print(a);
}
2
ответ дан 26 November 2019 в 23:49
поделиться
#include<stdio.h>
void print(int arr[][4], int s1, int s2) {
    int i, j;
    printf("\n");
    for(i = 0; i<s1; i++) {
        for(j = 0; j<s2; j++) {
            printf("%d, ", *((arr+i)+j));
        }
    }
    printf("\n");
}

int main() {
    int a[4][4] = {{0}};
    print(a,4,4);
}

Это будет работать, если под работой я имею в виду компиляцию. @AndreyT объяснил, почему ваша версия уже не работает.

Вот как следует передавать 2-мерный массив.

Для ясности вы также можете указать оба размера в объявлении функции:

#include<stdio.h>
void print(int arr[4][4], int s1, int s2) {
    int i, j;
    printf("\n");
    for(i = 0; i<s1; i++) {
        for(j = 0; j<s2; j++) {
            printf("%d, ", *((arr+i)+j));
        }
    }
    printf("\n");
}

int main() {
    int a[4][4] = {{0}};
    print(a,4,4);
}

Оба размера будут работать.

Вам также следует изменить * ((arr + i) + j) на a [i] [j] (желательно) или * (* (arr + i) + j) , если вы хотите получить доступ к j -й элемент строки i .

8
ответ дан 26 November 2019 в 23:49
поделиться

Вместо этого можно использовать int ** . Он гораздо более гибкий:

#include <stdio.h>
#include <stdlib.h>
void print(int **a, int numRows, int numCols )
{
  int row, col ;
  for( int row = 0; row < numRows; row++ )
  {
    for( int col = 0; col < numCols ; col++ )
    {
      printf("%5d, ", a[row][col]);
    }
    puts("");
  }
}

int main()
{
  int numRows = 16 ;
  int numCols = 5 ;
  int **a ;

  // a will be a 2d array with numRows rows and numCols cols

  // allocate an "array of arrays" of int
  a = (int**)malloc( numRows* sizeof(int*) ) ;

  // each entry in the array of arrays of int
  // isn't allocated yet, so allocate it
  for( int row = 0 ; row < numRows ; row++ )
  {
    // Allocate an array of int's, at each
    // entry in the "array of arrays"
    a[row] = (int*)malloc( numCols*sizeof(int) ) ;
  }

  int count = 1 ;
  for( int row = 0 ; row < numRows ; row++ )
  {
    for( int col = 0 ; col < numCols ; col++ )
    {
      a[row][col] = count++ ;
    }
  }

  print( a, numRows, numCols );
}

Еще одна вещь , которая может вас заинтересовать, - это такая структура, как D3DMATRIX :

typedef struct _D3DMATRIX {
    union {
        struct {
            float        _11, _12, _13, _14;
            float        _21, _22, _23, _24;
            float        _31, _32, _33, _34;
            float        _41, _42, _43, _44;

        };
        float m[4][4];
    };
} D3DMATRIX;

D3DMATRIX myMatrix ;

Самое приятное в этом маленьком лакомом кусочке то, что вы можете использовать оба myMatrix .m [0] [0] (для доступа к первому элементу), или вы можете использовать myMatrix._11 для доступа к тому же элементу. Союз - это секрет.

4
ответ дан 26 November 2019 в 23:49
поделиться

Помимо использования массивов переменной длины в C99, вы не можете переносимо написать функцию, принимающую многомерный массив, если размеры массивов неизвестны во время компиляции, см. ] Вопрос 6.19 из C-FAQ . Лучший способ справиться с этим - моделировать многомерные массивы с использованием динамически выделяемой памяти. Вопрос 6.16 очень хорошо объясняет детали этого.

1
ответ дан 26 November 2019 в 23:49
поделиться

Этот код не будет работать ни на C, ни на C ++. Массив типа int [4] [4] не может быть преобразован в указатель типа int ** (что означает int * arr [] для в объявлении параметра). Если вам удалось скомпилировать его на C, то это просто потому, что вы, вероятно, проигнорировали предупреждение компилятора C в основном того же формата, что и сообщение об ошибке, которое вы получили от компилятора C ++. (Иногда компиляторы C выдают предупреждения о том, что по сути является ошибкой .)

Итак, опять же, не делайте утверждений, которые не соответствуют действительности. Этот код не работает в C. Чтобы преобразовать встроенный 2D-массив в указатель int ** , вы можете использовать метод вроде этого

Преобразование многомерных массивов в указатели в C ++

(См. Принятый ответ. Проблема точно такая же.)

РЕДАКТИРОВАТЬ: Код появляется для работы на C, потому что другая ошибка в коде печати маскирует эффекты ошибки при передаче массива.Чтобы правильно получить доступ к элементу псевдо-массива int ** , вы должны использовать выражение * (* (arr + i) + j) или, лучше, простое arr [i] [j] (что одно и то же). Вы пропустили лишний * , из-за которого он печатал что-то, что не имеет абсолютно ничего общего с содержимым вашего массива. Опять же, инициализируйте свой массив в main чем-нибудь еще, чтобы убедиться, что результаты, которые вы печатаете на C, не имеют абсолютно ничего общего с вашим предполагаемым содержимым массива.

Если вы измените оператор printf , как показано выше, ваш код, скорее всего, выйдет из строя из-за ошибки передачи массива, которую я описал изначально.

Еще раз: вы не можете передать массив int [4] [4] как псевдо-массив int ** . Это то, что C ++ сообщает вам в сообщении об ошибке. И, я уверен, это то, что вам сказал ваш компилятор C, но вы, вероятно, проигнорировали это, поскольку это было «просто предупреждение».

30
ответ дан 26 November 2019 в 23:49
поделиться

Первое, что нужно сделать, это правильно выбрать типы. Если правила C ++ такие же, как и правила C в отношении типов массивов (я почти уверен, что это так), то с учетом объявления

int a[4][4];

выражение a имеет тип int [4] [4 ] , который неявно преобразуется («распадается») в тип указателя int (*) [4] (указатель на 4-элементный массив int) при передаче в print , поэтому вам нужно изменить print на

void print(int (*arr)[4], int s1, int s2)
{
  int i, j;        
  for(i = 0; i<s1; i++)        
    for(j = 0; j<s2; j++)        
      printf("%d, ", arr[i][j]);        
}        

Выражение arr [i] неявно разыменовывает arr , поэтому вам не нужно возиться с явное разыменование.

Недостатком является то, что print может обрабатывать только массивы Nx4 типа int; если вы хотите обрабатывать массивы других размеров, вам потребуется другой подход.

Вы можете вместо передачи массива передать адрес первого элемента и попросить print вручную вычислить смещения:

int main() {                    
  int a[4][4] = {{0}};                    
  print(&a[0][0],4,4);  // note how a is being passed                  
}  

void print(int *arr, int s1, int s2)  // note that arr is a simple int *
{
  int i, j;
  for (i = 0; i < s1; i++)
    for (j = 0; j < s2; j++)
      printf("%d, ", arr[i * s2 + j]);
}
0
ответ дан 26 November 2019 в 23:49
поделиться

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

void print(int arr[], int s1, int s2) {
...
printf("%d,", *(a+i + s2*j));
...
print((int*)a,4,4);

Это потребует лучшего ответа, объясняющего различия между арифметикой указателей и массивов в C и C++. Я не буду сейчас в это углубляться. Может быть, кто-то другой?

Я, очевидно, не шокирован тем же пунктом в вашем коде, что и другие плакаты. Больше всего меня беспокоит в заголовке функции print то, что вы используете двойное перенаправление для массива, где вы не собираетесь менять начальный указатель обратно (на самом деле это невозможно сделать, так как он является константой). Ответ @|V|lad исправляет это, устанавливая одно или два измерения в фиксированную константу, но тогда передача s1 и s2 становится бесполезной.

Все зависит от того, что вы действительно хотите сделать. Является ли print функцией печати массивов общего назначения или специализированной для некоторых типов массивов ?

0
ответ дан 26 November 2019 в 23:49
поделиться

Вот версия, которая работает, но теоретически недействительна (см. Ниже) C90 и C ++ 98:

#include <stdio.h>

static void print(int *arr, size_t s1, size_t s2)
{
    size_t i, j;
    printf("\n");
    for(i = 0; i < s1; i++) {
        for(j = 0; j < s2; j++) {
            printf("%d, ", arr[i * s2 + j]);
        }
    }
    printf("\n");
}

int main(void) {
    int a[4][4] = {{0}};
    print(a[0], 4, 4);
    return 0;
}

Версия C ++ с использованием шаблонов (адаптированная из ответа Notinlist ) может выглядеть следующим образом:

#include <iostream>
#include <cstring>

using namespace std;

template <size_t N, size_t M>
struct IntMatrix
{
    int data[N][M];
    IntMatrix() { memset(data, 0, sizeof data); }
};

template <size_t N, size_t M>
ostream& operator<<(ostream& out, const IntMatrix<N,M>& m)
{
    out << "\n";
    for(size_t i = 0; i < N; i++) {
        for(size_t j = 0; j < M; j++) {
            out << m.data[i][j] << ", ";
        }
    }
    out << "\n";
    return out;
}

int main()
{
    IntMatrix<4,4> a;
    cout << a;
    return 0;
}

В качестве альтернативы вы можете использовать вложенные контейнеры STL - например, vector > - вместо простого массива.

С C99 вы могли бы сделать

static void print(size_t s1, size_t s2, int arr[s1][s2]) {
    printf("\n");
    for(size_t i = 0; i < s1; i++) {
        for(size_t j = 0; j < s2; j++) {
            printf("%d, ", arr[i][j]);
        }
    }
    printf("\n");
}

и называть его как

print(4, 4, a);

Как Роберт указал в комментариях, первый фрагмент на самом деле включает неопределенное поведение. Однако, предполагая, что арифметика указателя всегда будет приводить к указателю, даже если задействовано неопределенное поведение (а не взорвать ваш компьютер), существует только один возможный результат из-за других ограничений в рамках стандарта, то есть это пример того, где Стандарт оставляет что-то необоснованно неопределенным.

Насколько я могу судить, замена

print(a[0], 4, 4);

на

union m2f { int multi[4][4]; int flat[16]; } *foo = (union m2f *)&a;
print(foo->flat, 4, 4);

сделает его допустимым C.

6
ответ дан 26 November 2019 в 23:49
поделиться

Проблема в том, что

int a[4][4];

будет фактически храниться в физически непрерывной памяти. Итак, чтобы получить доступ к произвольной части вашего массива 4x4, функция print должна знать размеры массива.Например, следующий небольшой фрагмент кода будет обращаться к одной и той же части памяти двумя разными способами.

#include <iostream>

void print(int a[][4]) {
    for (int i = 0; i <4; i++) {
        for (int j = 0; j < 4; j++) {
            //accessing as 4x4 array
            std::cout << a[i][j] <<std::endl;        

            //accessing corresponding to the physical layout in memory
            std::cout <<  *(*(a)+ i*4 + j) << std::endl;  

        }
    }
}

int main() {
    int a[4][4];

    //populating the array with the corresponding indices from 0 to 15
    int m = 0;
    for (int i = 0; i<4; i++) {
        for (int j= 0; j < 4; j++) {
            a[i][j] =  m;
            m++;
        }
    }
    print(a);
}

Таким образом, структура памяти не меняется, но меняется способ доступа. Это можно визуализировать как шахматную доску.

   0  1  2  3
  ----------
0| 1  2  3  4
1| 5  6  7  8
2| 9 10 11 12
3|13 14 15 16

Но настоящая физическая память выглядит так.

0*4+0 0*4+1 0*4+2 0*4+3 1*4+0 1*4+1 1*4+2 1*4+3 2*4+1   etc.
-----------------------------------------------------
1      2       3    4     5     6      7     8     9    etc.

В C ++ данные массива хранятся строка за строкой, и длина строки (в данном случае 4) всегда необходима для получения правильного смещения памяти для следующей строки. Таким образом, первый нижний индекс указывает только объем памяти, который требуется при объявлении массива, но больше не требуется для последующего вычисления смещения.

18
ответ дан 26 November 2019 в 23:49
поделиться