Как передать многомерный массив в функцию в C и C ++ - PullRequest
40 голосов
/ 13 мая 2010
#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 ++? Какие изменения необходимо внести?

Ответы [ 12 ]

29 голосов
/ 13 мая 2010

Этот код не будет работать на 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, но вы, вероятно, проигнорировали это, поскольку это было «просто предупреждение».

18 голосов
/ 13 мая 2010

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

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) всегда необходима, чтобы получить правильное смещение памяти для следующей строки. Первый нижний индекс поэтому указывает только объем памяти, который необходим при объявлении массива, но больше не нужен для вычисления смещения после этого.

8 голосов
/ 13 мая 2010
#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 объяснил, почему ваша версия уже не работает.

Вот как вы должны передать 2d массив.

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

#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.

6 голосов
/ 13 мая 2010

Вот версия, которая работает, но теоретически недействительна (см. Ниже) 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< vector<int> > - вместо простого массива.

С 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.

4 голосов
/ 13 мая 2010

Вместо этого вы можете использовать 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 для доступа к этому же элементу. союз является секретом.

2 голосов
/ 13 мая 2010
#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);
}
1 голос
/ 08 августа 2016

Многомерные массивы - это непрерывные блоки памяти. Таким образом, вы можете сделать это следующим образом:

#include <stdio.h>

void pa(const int *a, int y, int x)
{
    int i, j;
    for (i=0;i<y;i++)
    {
        for (j=0;j<x;j++)
            printf("%i", *(a+j+i*x));
        printf("\n");
    }
}

int main()
{
    int a[4][3] = { {1,2,3},
                    {4,5,6},
                    {4,5,6},
                    {7,8,9} };

    pa(a[0], 4, 3);

    return 0;
}

Это также работает в C ++;

1 голос
/ 13 мая 2010

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

0 голосов
/ 10 ноября 2017

Я просто хочу показать версию ответа Бобобо на языке C ++.

int numRows = 16 ;
int numCols = 5 ;
int **a ;

a = new int*[ numRows* sizeof(int*) ];
for( int row = 0 ; row < numRows ; row++ )
{
   a[row] = new int[ numCols*sizeof(int) ];
}

Остальной код такой же, как у бобобо.

0 голосов
/ 08 февраля 2014
#include<stdio.h>
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]);
}

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

это скомпилирует редактирование: кто-то уже опубликовал это решение, мой плохой

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