wake-up-neo.com

Übergabe eines 2D-Arrays an eine C ++ - Funktion

Ich habe eine Funktion, die ich als Parameter ein 2D-Array mit variabler Größe nehmen möchte.

Bisher habe ich das:

void myFunction(double** myArray){
     myArray[x][y] = 5;
     etc...
}

Und ich habe ein Array an anderer Stelle in meinem Code deklariert:

double anArray[10][10];

Das Aufrufen von myFunction(anArray) gibt mir jedoch einen Fehler.

Ich möchte das Array nicht kopieren, wenn ich es weitergebe. Alle in myFunction vorgenommenen Änderungen sollten den Status von anArray ändern. Wenn ich das richtig verstehe, möchte ich nur einen Zeiger auf ein 2D-Array als Argument übergeben. Die Funktion muss auch Arrays unterschiedlicher Größe akzeptieren. Zum Beispiel [10][10] und [5][5]. Wie kann ich das machen?

281
RogerDarwin

Es gibt drei Möglichkeiten, ein 2D-Array an eine Funktion zu übergeben:

  1. Der Parameter ist ein 2D-Array

    int array[10][10];
    void passFunc(int a[][10])
    {
        // ...
    }
    passFunc(array);
    
  2. Der Parameter ist ein Array mit Zeigern

    int *array[10];
    for(int i = 0; i < 10; i++)
        array[i] = new int[10];
    void passFunc(int *a[10]) //Array containing pointers
    {
        // ...
    }
    passFunc(array);
    
  3. Der Parameter ist ein Zeiger auf einen Zeiger

    int **array;
    array = new int *[10];
    for(int i = 0; i <10; i++)
        array[i] = new int[10];
    void passFunc(int **a)
    {
        // ...
    }
    passFunc(array);
    
384
shengy

Feste Größe

1. Referenz übergeben

template <size_t rows, size_t cols>
void process_2d_array_template(int (&array)[rows][cols])
{
    std::cout << __func__ << std::endl;
    for (size_t i = 0; i < rows; ++i)
    {
        std::cout << i << ": ";
        for (size_t j = 0; j < cols; ++j)
            std::cout << array[i][j] << '\t';
        std::cout << std::endl;
    }
}

In C++ ist es wahrscheinlich am sichersten, das Array als Referenz zu übergeben, ohne die Dimensionsinformationen zu verlieren, da der Aufrufer keine falsche Dimension übergeben muss (Compiler-Flags bei Nichtübereinstimmung). Bei dynamischen Arrays (Freestore-Arrays) ist dies jedoch nicht möglich. es funktioniert nur für automatische ( normalerweise Stack-Living ) Arrays, d. h. die Dimensionalität sollte zur Kompilierungszeit bekannt sein.

2. Zeiger übergeben

void process_2d_array_pointer(int (*array)[5][10])
{
    std::cout << __func__ << std::endl;
    for (size_t i = 0; i < 5; ++i)
    {
        std::cout << i << ": ";
        for (size_t j = 0; j < 10; ++j)
            std::cout << (*array)[i][j] << '\t';
        std::cout << std::endl;
    }    
}

Das C-Äquivalent der vorherigen Methode übergibt das Array als Zeiger. Dies ist nicht zu verwechseln mit der Übergabe des zerfallenen Zeigertyps (3) des Arrays, der gängigen, aber weniger sicheren Methode flexibler. Verwenden Sie diese Methode wie (1) , wenn alle Dimensionen des Arrays zur Kompilierungszeit festgelegt und bekannt sind. Beachten Sie, dass beim Aufruf der Funktion die Adresse des Arrays process_2d_array_pointer(&a) und nicht die Adresse des ersten Elements durch Zerfall process_2d_array_pointer(a) übergeben werden sollte.

Variable Größe

Diese werden von C geerbt, sind jedoch weniger sicher. Der Compiler hat keine Möglichkeit zu überprüfen, um sicherzustellen, dass der Aufrufer die erforderlichen Dimensionen übergibt. Die Funktion basiert nur auf dem, was der Anrufer als Dimension (en) weitergibt. Diese sind flexibler als die oben genannten, da ihnen immer Arrays unterschiedlicher Länge übergeben werden können.

Es ist zu beachten, dass es in C nicht möglich ist, ein Array direkt an eine Funktion zu übergeben [während sie in C++ als Referenz übergeben werden können (1) ]; (2) übergibt einen Zeiger auf das Array und nicht auf das Array selbst. Wenn Sie ein Array immer so übergeben, wie es ist, wird dies zu einer Zeigerkopieroperation, die durch das Zerfallen des Arrays in einen Zeiger erleichtert wird.

3. Übergebe (Wert) einen Zeiger auf den verfallenen Typ

// int array[][10] is just fancy notation for the same thing
void process_2d_array(int (*array)[10], size_t rows)
{
    std::cout << __func__ << std::endl;
    for (size_t i = 0; i < rows; ++i)
    {
        std::cout << i << ": ";
        for (size_t j = 0; j < 10; ++j)
            std::cout << array[i][j] << '\t';
        std::cout << std::endl;
    }
}

Obwohl int array[][10] zulässig ist, würde ich es nicht über die obige Syntax empfehlen, da die obige Syntax deutlich macht, dass der Bezeichner array ein einzelner Zeiger auf ein Array von 10 Ganzzahlen ist, während diese Syntax sieht wie ein 2D-Array aus, ist aber der gleiche Zeiger auf ein Array mit 10 ganzen Zahlen. Hier kennen wir die Anzahl der Elemente in einer einzelnen Zeile (d. H. Die Spaltengröße, hier 10), aber die Anzahl der Zeilen ist unbekannt und daher als Argument zu übergeben. In diesem Fall besteht eine gewisse Sicherheit, da der Compiler markieren kann, wenn ein Zeiger auf ein Array mit einer zweiten Dimension ungleich 10 übergeben wird. Die erste Dimension ist der variierende Teil und kann weggelassen werden. Siehe hier für die Begründung warum nur die erste Dimension weggelassen werden darf.

4. Zeiger an Zeiger übergeben

// int *array[10] is just fancy notation for the same thing
void process_pointer_2_pointer(int **array, size_t rows, size_t cols)
{
    std::cout << __func__ << std::endl;
    for (size_t i = 0; i < rows; ++i)
    {
        std::cout << i << ": ";
        for (size_t j = 0; j < cols; ++j)
            std::cout << array[i][j] << '\t';
        std::cout << std::endl;
    }
}

Wieder gibt es eine alternative Syntax von int *array[10], die mit int **array identisch ist. In dieser Syntax wird der [10] ignoriert, da er in einen Zeiger zerfällt und dadurch zu int **array wird. Vielleicht ist es nur ein Hinweis für den Aufrufer, dass das übergebene Array mindestens 10 Spalten haben sollte, auch dann ist eine Zeilenanzahl erforderlich. In jedem Fall markiert der Compiler keine Längen-/Größenverstöße (er prüft nur, ob der übergebene Typ ein Zeiger auf Zeiger ist). Daher ist es hier sinnvoll, sowohl Zeilen- als auch Spaltenzähler als Parameter zu verwenden.

Hinweis: (4) ist die am wenigsten sichere Option , da es kaum und am häufigsten eine Typprüfung gibt ungünstig. Man kann ein 2D-Array nicht legitimerweise an diese Funktion übergeben. C-FAQ verurteilt die übliche Problemumgehung, int x[5][10]; process_pointer_2_pointer((int**)&x[0][0], 5, 10); auszuführen, da dies möglicherweise zu undefiniertem Verhalten führt aufgrund der Abflachung des Arrays. Die richtige Art, ein Array in diesem Verfahren zu übergeben, bringt uns zum unbequemen Teil, d. H. Wir benötigen ein zusätzliches (Ersatz-) Array von Zeigern, wobei jedes Element auf die jeweilige Zeile des tatsächlichen, zu übergebenden Arrays zeigt; Dieses Surrogat wird dann an die Funktion übergeben (siehe unten). all dies, um die gleiche Arbeit zu erledigen wie die oben genannten Methoden, die sicherer, sauberer und vielleicht schneller sind.

Hier ist ein Treiberprogramm zum Testen der obigen Funktionen:

#include <iostream>

// copy above functions here

int main()
{
    int a[5][10] = { { } };
    process_2d_array_template(a);
    process_2d_array_pointer(&a);    // <-- notice the unusual usage of addressof (&) operator on an array
    process_2d_array(a, 5);
    // works since a's first dimension decays into a pointer thereby becoming int (*)[10]

    int *b[5];  // surrogate
    for (size_t i = 0; i < 5; ++i)
    {
        b[i] = a[i];
    }
    // another popular way to define b: here the 2D arrays dims may be non-const, runtime var
    // int **b = new int*[5];
    // for (size_t i = 0; i < 5; ++i) b[i] = new int[10];
    process_pointer_2_pointer(b, 5, 10);
    // process_2d_array(b, 5);
    // doesn't work since b's first dimension decays into a pointer thereby becoming int**
}
153
legends2k

Als Modifikation von Shengys erstem Vorschlag können Sie Vorlagen verwenden, damit die Funktion eine mehrdimensionale Array-Variable akzeptiert (anstatt ein Array von Zeigern zu speichern, die verwaltet und gelöscht werden müssen):

template <size_t size_x, size_t size_y>
void func(double (&arr)[size_x][size_y])
{
    printf("%p\n", &arr);
}

int main()
{
    double a1[10][10];
    double a2[5][5];

    printf("%p\n%p\n\n", &a1, &a2);
    func(a1);
    func(a2);

    return 0;
}

Die print-Anweisungen sollen zeigen, dass die Arrays als Referenz übergeben werden (indem die Adressen der Variablen angezeigt werden).

40
Zrax

Sie können eine Funktionsvorlage wie folgt erstellen:

template<int R, int C>
void myFunction(double (&myArray)[R][C])
{
    myArray[x][y] = 5;
    etc...
}

Dann haben Sie beide Dimensionsgrößen über R und C. Für jede Arraygröße wird eine andere Funktion erstellt. Wenn Ihre Funktion also groß ist und Sie sie mit einer Vielzahl unterschiedlicher Arraygrößen aufrufen, kann dies kostspielig sein. Sie können es jedoch als Wrapper über eine Funktion wie diese verwenden:

void myFunction(double * arr, int R, int C)
{
    arr[x * C + y] = 5;
    etc...
}

Es behandelt das Array als eindimensional und verwendet Arithmetik, um die Offsets der Indizes zu ermitteln. In diesem Fall würden Sie die Vorlage folgendermaßen definieren:

template<int C, int R>
void myFunction(double (&myArray)[R][C])
{
    myFunction(*myArray, R, C);
}
20

Überrascht, dass dies noch niemand erwähnt hat, aber Sie können einfach eine Vorlage für eine 2D-unterstützende [] [] Semantik erstellen.

template <typename TwoD>
void myFunction(TwoD& myArray){
     myArray[x][y] = 5;
     etc...
}

// call with
double anArray[10][10];
myFunction(anArray);

Es funktioniert mit jeder "Array-ähnlichen" 2D-Datenstruktur wie std::vector<std::vector<T>> oder einem benutzerdefinierten Typ, um die Wiederverwendung von Code zu maximieren.

16
LemonPi

anArray[10][10] ist kein Zeiger auf einen Zeiger, sondern ein zusammenhängender Speicherbereich, in dem 100 Werte des Typs double gespeichert werden können. Dieser Compiler weiß, wie er zu adressieren ist, weil Sie die Dimensionen angegeben haben. Sie müssen es als Array an eine Funktion übergeben. Sie können die Größe der anfänglichen Dimension wie folgt weglassen:

void f(double p[][10]) {
}

Auf diese Weise können Sie jedoch keine Arrays mit einer anderen Dimension als zehn übergeben.

Die beste Lösung in C++ ist die Verwendung von std::vector<std::vector<double> >: Es ist fast genauso effizient und wesentlich komfortabler.

10
dasblinkenlight

Sie können so etwas tun ...

#include<iostream>

using namespace std;

//for changing values in 2D array
void myFunc(double *a,int rows,int cols){
    for(int i=0;i<rows;i++){
        for(int j=0;j<cols;j++){
            *(a+ i*rows + j)+=10.0;
        }
    }
}

//for printing 2D array,similar to myFunc
void printArray(double *a,int rows,int cols){
    cout<<"Printing your array...\n";
    for(int i=0;i<rows;i++){
        for(int j=0;j<cols;j++){
            cout<<*(a+ i*rows + j)<<"  ";
        }
    cout<<"\n";
    }
}

int main(){
    //declare and initialize your array
    double a[2][2]={{1.5 , 2.5},{3.5 , 4.5}};

    //the 1st argument is the address of the first row i.e
    //the first 1D array
    //the 2nd argument is the no of rows of your array
    //the 3rd argument is the no of columns of your array
    myFunc(a[0],2,2);

    //same way as myFunc
    printArray(a[0],2,2);

    return 0;
}

Ihre Ausgabe wird wie folgt sein ...

11.5  12.5
13.5  14.5
8
Sagar Shah

Das eindimensionale Array verwandelt sich in einen Zeiger, der auf das erste Element im Array zeigt. Während ein 2D-Array zu einem Zeiger zerfällt, der auf die erste Zeile zeigt. Der Funktionsprototyp sollte also sein:

void myFunction(double (*myArray) [10]);

Ich würde std::vector rohen Arrays vorziehen.

8
Mahesh

Hier ist ein Beispiel eines Vektormatrixvektors

#include <iostream>
#include <vector>
using namespace std;

typedef vector< vector<int> > Matrix;

void print(Matrix& m)
{
   int M=m.size();
   int N=m[0].size();
   for(int i=0; i<M; i++) {
      for(int j=0; j<N; j++)
         cout << m[i][j] << " ";
      cout << endl;
   }
   cout << endl;
}


int main()
{
    Matrix m = { {1,2,3,4},
                 {5,6,7,8},
                 {9,1,2,3} };
    print(m);

    //To initialize a 3 x 4 matrix with 0:
    Matrix n( 3,vector<int>(4,0));
    print(n);
    return 0;
}

ausgabe:

1 2 3 4
5 6 7 8
9 1 2 3

0 0 0 0
0 0 0 0
0 0 0 0
4
edW

Es gibt verschiedene Möglichkeiten, ein 2D-Array an eine Funktion zu übergeben:

  • Verwenden eines einzelnen Zeigers Wir müssen das 2D-Array typisieren.

    #include<bits/stdc++.h>
    using namespace std;
    
    
    void func(int *arr, int m, int n)
    {
        for (int i=0; i<m; i++)
        {
           for (int j=0; j<n; j++)
           {
              cout<<*((arr+i*n) + j)<<" ";
           }
           cout<<endl;
        }
    }
    
    int main()
    {
        int m = 3, n = 3;
        int arr[m][n] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
        func((int *)arr, m, n);
        return 0;
    }
    
  • Mit Doppelzeiger Auf diese Weise typisieren wir auch das 2d-Array

    #include<bits/stdc++.h>
    using namespace std;

   void func(int **arr, int row, int col)
   {
      for (int i=0; i<row; i++)
      {
         for(int j=0 ; j<col; j++)
         {
           cout<<arr[i][j]<<" ";
         }
         printf("\n");
      }
   }

  int main()
  {
     int row, colum;
     cin>>row>>colum;
     int** arr = new int*[row];

     for(int i=0; i<row; i++)
     {
        arr[i] = new int[colum];
     }

     for(int i=0; i<row; i++)
     {
         for(int j=0; j<colum; j++)
         {
            cin>>arr[i][j];
         }
     }
     func(arr, row, colum);

     return 0;
   }
2
rashedcs

Eine wichtige Sache für die Übergabe mehrdimensionaler Arrays ist:

  • First array dimension muss nicht angegeben werden.
  • Second(any any further)dimension muss angegeben werden.

1.Wenn nur die zweite Dimension global verfügbar ist (entweder als Makro oder als globale Konstante

`const int N = 3;

`void print(int arr[][N], int m)
{
int i, j;
for (i = 0; i < m; i++)
  for (j = 0; j < N; j++)
    printf("%d ", arr[i][j]);
}`

int main()
{
int arr[][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
print(arr, 3);
return 0;
}`

2.Verwenden eines einzelnen Zeigers: Bei dieser Methode müssen Sie das 2D-Array typisieren, wenn Sie an function übergeben.

`void print(int *arr, int m, int n)
{
int i, j;
for (i = 0; i < m; i++)
  for (j = 0; j < n; j++)
    printf("%d ", *((arr+i*n) + j));
 }

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

// We can also use "print(&arr[0][0], m, n);"
print((int *)arr, m, n);
return 0;
}`
1
sonorous

In dem Fall, dass Sie ein dynamisches 2D-Array an eine Funktion übergeben möchten, können einige Zeiger hilfreich sein.

void func1(int *arr, int n, int m){
    ...
    int i_j_the_element = arr[i * m + j];  // use the idiom of i * m + j for arr[i][j] 
    ...
}

void func2(){
    ...
    int arr[n][m];
    ...
    func1(&(arr[0][0]), n, m);
}
0

Sie können die Vorlagenfunktion in C++ verwenden, um dies zu tun. Ich habe so etwas gemacht:

template<typename T, size_t col>
T process(T a[][col], size_t row) {
...
}

das Problem bei diesem Ansatz ist, dass für jeden Wert von col, den Sie bereitstellen, eine neue Funktionsdefinition unter Verwendung der Vorlage instanziiert wird. damit,

int some_mat[3][3], another_mat[4,5];
process(some_mat, 3);
process(another_mat, 4);

instanziiert die Vorlage zweimal, um zwei Funktionsdefinitionen zu erstellen (eine mit col = 3 und eine mit col = 5).

0
vantony

Wenn Sie int a[2][3] an void func(int** pp) übergeben möchten, benötigen Sie die folgenden Hilfsschritte.

int a[2][3];
int* p[2] = {a[0],a[1]};
int** pp = p;

func(pp);

Da der erste [2] implizit angegeben werden kann, kann er wie folgt weiter vereinfacht werden.

int a[][3];
int* p[] = {a[0],a[1]};
int** pp = p;

func(pp);