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

Как передать многомерный массив функции в 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, но не в С++.

Ошибка:

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

Почему это не работает на С++? Какие изменения необходимо сделать?

4b9b3361

Ответ 1

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

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

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

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

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

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

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

Ответ 2

Проблема заключается в том, что

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.

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

Ответ 3

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

Ответ 4

Здесь версия, которая работает, но теоретически недействительна (см. ниже) C90 и С++ 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;
}

Версия С++ с использованием шаблонов (адаптирована из Ответ на вопросник) может выглядеть так:

#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);

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

Насколько я могу судить, подставляя

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

с

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

сделает его законным.

Ответ 5

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

Ответ 6

#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);
}

Ответ 7

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

Ответ 8

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

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

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

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

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

Ответ 9

Первое, что нужно сделать, это получить правильные типы. Если правила С++ совпадают с 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]);
}

Ответ 10

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

#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;
}

Он также работает в С++;

Ответ 11

Я просто хочу показать версию ответа bobobobo на С++.

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) ];
}

Остальная часть кода такая же, как у bobobobo.

Ответ 12

#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);
}

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