wake-up-neo.com

Wie man ein multidimensionales Array an eine Funktion in C und C++ weitergibt

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

Dies funktioniert in C, aber nicht in C++.

error:

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

Warum funktioniert es nicht in C++? Welche Änderung muss vorgenommen werden?

37
Moeb

Dieser Code wird in C oder C++ nicht funktionieren. Ein Array vom Typ int[4][4] kann nicht in einen Zeiger vom Typ int ** konvertiert werden (wofür int *arr[] in der Parameterdeklaration steht). Wenn Sie es geschafft haben, compile es in C zu erstellen, liegt dies einfach daran, dass Sie wahrscheinlich eine C-Compiler-Warnung ignoriert haben, die im Wesentlichen das gleiche Format hat wie die Fehlermeldung, die Sie vom C++ - Compiler erhalten haben. (Manchmal geben C-Compiler Warnungen aus, was im Wesentlichen ein Fehler ist.)

Machen Sie also keine Behauptungen, die nicht wahr sind. Dieser Code funktioniert nicht in C. Um ein eingebautes 2D-Array in einen int **-Zeiger zu konvertieren, können Sie eine Technik wie diese verwenden

Konvertieren von mehrdimensionalen Arrays in C++ in Zeiger

(Siehe akzeptierte Antwort. Das Problem ist genau das gleiche.)

EDIT: Der Code erscheint für die Arbeit in C, da ein anderer Fehler im Druckcode die Auswirkungen des Fehlers beim Übergeben von Arrays maskiert. Um korrekt auf ein Element eines int **-Pseudo-Arrays zugreifen zu können, müssen Sie den Ausdruck *(*(arr + i) + j) oder besser ein einfaches arr[i][j] verwenden (was dasselbe ist). Sie haben das zusätzliche * verpasst, wodurch es etwas gedruckt hat, das absolut nichts mit dem Inhalt Ihres Arrays zu tun hat. Initialisieren Sie Ihr Array in main erneut mit etwas anderem, um zu sehen, dass die Ergebnisse, die Sie in C drucken, absolut nichts mit dem beabsichtigten Inhalt des Arrays zu tun haben.

Wenn Sie die printf-Anweisung wie oben gezeigt ändern, stürzt der Code höchstwahrscheinlich aufgrund des von mir anfangs beschriebenen Array-Passing-Fehlers ab.

Noch einmal: Sie können ein int[4][4]-Array nicht als int **-Pseudo-Array übergeben. Das sagt Ihnen C++ in der Fehlermeldung. Ich bin sicher, das hat Ihnen Ihr C-Compiler gesagt, aber Sie haben es wahrscheinlich ignoriert, da es "nur eine Warnung" war.

27
AnT

Das Problem ist, dass 

int a[4][4];

wird tatsächlich in einem physisch kontinuierlichen Speicher gespeichert. Um auf einen beliebigen Teil Ihres 4x4-Arrays zugreifen zu können, muss die Funktion "print" die Abmessungen des Arrays kennen. Der folgende kleine Code greift beispielsweise auf zwei verschiedene Arten auf denselben Speicherbereich zu. 

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

Das Speicherlayout ändert sich also nicht, der Zugriff wird jedoch geändert. Es kann wie ein Schachbrett visualisiert werden.

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

Aber das reale physische Gedächtnis sieht so aus. 

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.

In c ++ werden die Daten eines Arrays Zeile für Zeile gespeichert, und die Länge einer Zeile (in diesem Fall 4) ist immer erforderlich, um den richtigen Speicheroffset für die nächste Zeile zu erhalten. Der erste Index gibt daher nur die Menge an Speicher an, die bei der Deklaration des Arrays benötigt wird, ist aber nicht mehr erforderlich, um den Offset danach zu berechnen. 

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

Das wird funktionieren, wo ich mit Arbeit kompiliere. @AndreyT hat erklärt, warum Ihre Version nicht bereits funktioniert.

So sollten Sie ein 2D-Array übergeben.

Zur besseren Übersicht können Sie auch beide Größen in der Funktionsdeklaration angeben:

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

Beides wird funktionieren.

Sie sollten auch *((arr+i)+j) in a[i][j] (vorzugsweise) oder *(*(arr+i)+j) ändern, wenn Sie auf das jth-Element der Zeile i zugreifen möchten.

8
IVlad

Hier ist eine Version, die funktioniert, aber theoretisch ungültig ist (siehe unten) C90 und 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;
}

Eine C++ - Version mit Templates (angepasst an Notinlist's answer ) könnte folgendermaßen aussehen:

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

Alternativ können Sie geschachtelte STL-Container - dh vector< vector<int> > - anstelle eines einfachen Arrays verwenden.

Mit C99 könnten Sie das tun

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

und nennen es als

print(4, 4, a);

Wie Robert in den Kommentaren betonte, beinhaltet der erste Ausschnitt tatsächlich undefiniertes Verhalten. Vorausgesetzt jedoch, dass Zeigerarithmetik immer zu einem Zeiger führt, selbst wenn undefiniertes Verhalten involviert ist (und nicht Ihren Computer in die Luft sprengt), gibt es aufgrund anderer Einschränkungen innerhalb des Standards nur ein einziges mögliches Ergebnis, d. H Standard lässt etwas unnötig undefiniert.

Soweit ich das beurteilen kann, ersetzen

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

mit

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

wird es legal machen C.

6
Christoph

Sie können stattdessen int** verwenden. Es ist viel flexibler:

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

Etwas anderes für die Sie sich interessieren könnten, ist eine Struktur wie D3DMATRIX :

 typedef struct _D3DMATRIX {
 Gewerkschaft {
 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; 

Das Schöne an diesem kleinen Leckerbissen ist, dass Sie sowohl myMatrix.m[0][0] (für das erste Element) als auch myMatrix._11 für dasselbe Element verwenden können. Die union ist das Geheimnis.

4
bobobobo
#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
Notinlist

Abgesehen von der Verwendung von Arrays mit variabler Länge in C99 können Sie nicht wirklich portably eine Funktion schreiben, um ein mehrdimensionales Array zu akzeptieren, wenn die Größe der Arrays zur Kompilierzeit nicht bekannt ist. Siehe Frage 6.19 von die C-FAQ . Am besten damit umgehen Sie die Simulation mehrdimensionaler Arrays mit dynamisch zugewiesenem Speicher. Frage 6.16 erklärt sehr gut, wie man dies ausführt.

1
Robert Gamble

Mehrdimensionale Arrays sind kontinuierliche Speicherblöcke. So kannst du es so machen:

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

Es funktioniert auch in C++;

0
it-west.net

Zuerst müssen Sie die Typen richtig einstellen. Wenn die C++ - Regeln in Bezug auf Array-Typen den C-Regeln entsprechen (ich bin mir ziemlich sicher, dass sie es sind), dann geben Sie die Deklaration an

int a[4][4];

der Ausdruck a hat den Typ int [4][4], der implizit in einen Zeigertyp von int (*)[4] (Zeiger auf ein 4-Element-Array von int) umgewandelt wird, wenn er an print übergeben wird. Daher müssen Sie print in ändern

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

Der Ausdruck arr[i] definiert implizit die Variable arr, Sie müssen sich also nicht mit einer expliziten Dereferenzierung herumschlagen. 

Der Nachteil ist, dass print nur Nx4-Arrays von int verarbeiten kann. Wenn Sie mit anderen Array-Größen arbeiten möchten, müssen Sie einen anderen Ansatz verwenden. 

Anstatt das Array zu übergeben, können Sie die Adresse des ersten Elements übergeben und print die Offsets manuell berechnen:

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

das wird compilieren kompilieren: jemand hat diese Lösung schon mal schlecht geschrieben

0
devmabbott

Kurze Antwort, Sie können das Programm wie folgt ändern

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

Dies würde eine bessere Antwort erfordern, um die Unterschiede zwischen Zeiger- und Zeigerarithmetik und Arrays in C und C++ zu erklären. Ich werde jetzt nicht damit anfangen. Vielleicht jemand anderes?

Ich bin natürlich nicht schockiert über den gleichen Punkt wie andere Poster in Ihrem Code. Am meisten stört mich im Header der Druckfunktion, dass Sie eine doppelte Indirektion für ein Array verwenden, bei dem Sie den anfänglichen Zeiger nicht zurück ändern möchten. @ | V | lad antworten Sie können dieses Problem beheben, indem Sie eine oder zwei Dimensionen auf eine feste Konstante setzen. 

Alles hängt davon ab, was Sie wirklich tun möchten. Ist Drucken eine allgemeine Array-Druckfunktion oder eine spezielle für einige Array-Typen?

0
kriss