Een 2D-array doorgeven aan een C++ -functie

Ik heb een functie die ik wil nemen, als parameter, een 2D-reeks van variabele grootte.

Tot nu toe heb ik dit:

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

en ik heb een array verklaard elders in mijn code:

double anArray[10][10];

Bellen echter myFunction(anArray)geeft me een fout.

Ik wil de array niet kopiëren wanneer ik het inpasseer. Alle wijzigingen in myFunctionmoeten de status van anArraywijzigen. Als ik het goed begrijp, wil ik alleen doorgeven als een argument een aanwijzer naar een 2D-array. De functie moet ook arrays van verschillende groottes accepteren. Dus bijvoorbeeld [10][10]en [5][5]. Hoe kan ik dit doen?


Antwoord 1, Autoriteit 100%

Er zijn drie manieren om een ​​2D-array door te geven aan een functie:

  1. De parameter is een 2D-array

    int array[10][10];
    void passFunc(int a[][10])
    {
        // ...
    }
    passFunc(array);
    
  2. De parameter is een array met pointers

    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. De parameter is een aanwijzer naar een aanwijzer

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

Antwoord 2, Autoriteit 43%

Vaste grootte

1. Passeren per referentie

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++ passeren van de array door middel van het verliezen van de dimensie-informatie is waarschijnlijk de veiligste, omdat iemand zich geen zorgen hoeft te maken over de beller die een onjuiste dimensie passeert (compiler-vlaggen bij mismatching). Dit is echter niet mogelijk met dynamische (freestore) arrays; Het werkt voor automatisch (meestal stapel-leven arrays alleen d.w.z. de dimensionaliteit moet bekend zijn bij compileertijd.

2. Passeren door aanwijzer

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

Het C-equivalent van de vorige methode passeert de array per aanwijzer. Dit mag niet worden verward met het passeren van het vervallen aanwijstype (3) , dat de gewone, populaire methode is, zij het minder veilig dan deze, maar flexibeler. Zoals (1) , gebruik deze methode wanneer alle afmetingen van de array zijn vastgesteld en bekend bij Compile-Time. Merk op dat bij het bellen van de functie het adres van de array moet worden aangenomen process_2d_array_pointer(&a)en niet het adres van het eerste element van verval process_2d_array_pointer(a).

Variabele maat

Deze worden geërfd van C, maar zijn minder veilig, de compiler heeft geen manier om te controleren, te garanderen dat de beller de vereiste dimensies passeert. De functie is alleen banken over wat de beller passeert als de dimensie (s). Deze zijn flexibeler dan de bovengenoemde omdat arrays met verschillende lengtes onveranderlijk kunnen worden doorgegeven aan hen.

Er moet aan worden herinnerd dat er niet zoiets bestaat als een array rechtstreeks doorgegeven aan een functie in C [terwijl ze in C++ kunnen worden doorgegeven als een referentie (1) ]; (2) passeert een aanwijzer naar de array en niet de array zelf. Altijd passeren van een array als-wordt wordt een pointer-kopieerbewerking die wordt vergemakkelijkt door array’s Aard van vervallen in een aanwijzer .

3. Passeren (waarde) Een aanwijzer naar het vervallen type

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

Hoewel int array[][10]is toegestaan, zou ik het niet aanbevelen boven de bovenstaande syntaxis, aangezien de bovenstaande syntaxis duidelijk maakt dat de identifier arrayis een enkele aanwijzer naar een array van 10 gehele getallen, terwijl deze syntaxis eruit zietalsof het een 2D-array is, maar dezelfde aanwijzer is naar een array van 10 gehele getallen. Hier kennen we het aantal elementen in een enkele rij (d.w.z. de kolomgrootte, hier 10), maar het aantal rijen is onbekend en moet daarom als argument worden doorgegeven. In dit geval is er enige veiligheid omdat de compiler kan markeren wanneer een aanwijzer naar een array met een tweede dimensie die niet gelijk is aan 10 wordt doorgegeven. De eerste dimensie is het variërende deel en kan worden weggelaten. Zie hier de redeneringwaarom alleen de eerste dimensie mag worden weggelaten.

4. Ga met de aanwijzer naar een aanwijzer

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

Wederom is er een alternatieve syntaxis van int *array[10]die hetzelfde is als int **array. In deze syntaxis wordt de [10]genegeerd, aangezien deze vervalt tot een aanwijzer, waardoor het int **arraywordt. Misschien is het slechts een aanwijzing voor de beller dat de doorgegeven array ten minste 10 kolommen moet hebben, zelfs dan is het aantal rijen vereist. In ieder geval signaleert de compiler geen overtredingen van lengte/grootte (hij controleert alleen of het doorgegeven type een pointer naar pointer is), en daarom is het zinvol om hier zowel rij- als kolomtellingen als parameter te vereisen.

Opmerking:(4) is de minst veilige optieomdat het nauwelijks typecontrole heeft en het meest onhandig is. Men kan niet legitiem een 2D-array doorgeven aan deze functie; C-FAQ veroordeeltde gebruikelijke oplossing voor het doen van int x[5][10]; process_pointer_2_pointer((int**)&x[0][0], 5, 10);aangezien het mogelijk leiden tot ongedefinieerd gedragals gevolg van afvlakking van de array. De juiste manier om een array in deze methode door te geven, brengt ons bij het lastige deel, d.w.z. we hebben een extra (surrogaat) array van pointers nodig waarbij elk van zijn elementen naar de respectieve rij van de daadwerkelijke, door te geven array verwijst; deze surrogaat wordt vervolgens doorgegeven aan de functie (zie hieronder); dit alles om dezelfde klus te klaren als de bovenstaande methoden die veiliger, schoner en misschien sneller zijn.

Hier is een stuurprogramma om de bovenstaande functies te testen:

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

Antwoord 3, Autoriteit 9%

Een aanpassing aan de eerste suggestie van Shengy, u kunt sjablonen gebruiken om de functie een multi-dimensionale array-variabele accepteert (in plaats van een reeks aanwijzingen op te slaan die moeten worden beheerd en verwijderd):

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

De afdrukafschriften zijn er om aan te tonen dat de arrays worden doorgegeven aan de hand (door de adressen van de variabelen)

weer te geven


Antwoord 4, Autoriteit 6%

Verbaasd dat niemand dit al heeft genoemd, maar je kunt gewoon sjabloon op alles 2D-ondersteunende [] [] semantiek.

template <typename TwoD>
void myFunction(TwoD& myArray){
     myArray[x][y] = 5;
     etc...
}
// call with
double anArray[10][10];
myFunction(anArray);

Het werkt met elke 2D “array-achtige” datastructuur, zoals std::vector<std::vector<T>>of een door de gebruiker gedefinieerde type om de code opnieuw te maximaliseren.


Antwoord 5, Autoriteit 4%

U kunt een functiesjabloon maken zoals deze:

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

Dan heb je beide dimensiegroottes via R en C. Er wordt een andere functie gemaakt voor elke array-formaat, dus als je functie groot is en je het belt met een verscheidenheid aan verschillende array-maten, kan dit kostbaar zijn. Je zou het kunnen gebruiken als een wrapper over een functie als deze:

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

Het behandelt de array als een dimensionaal en gebruikt rekenkundig om de compensaties van de indexen te achterhalen. In dit geval zou u de sjabloon als volgt definiëren:

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

Antwoord 6, Autoriteit 3%

anArray[10][10]is geen aanwijzer naar een aanwijzer, het is een aaneengesloten stuk geheugen dat geschikt is voor het opslaan van 100 waarden van het type dubbele, welke compiler weet hoe u moet worden aangepakt de dimensies. Je moet het doorgeven aan een functie als een array. U kunt de grootte van de initiële dimensie weglaten, als volgt:

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

Dit laat je echter geen arrays passeren met de laatste dimensie anders dan tien.

De beste oplossing in C++ is om std::vector<std::vector<double> >: het is bijna net zo efficiënt en aanzienlijk handiger.


Antwoord 7, autoriteit 2%

Eendimensionale array vervalt tot een aanwijzer die naar het eerste element in de array wijst. Terwijl een 2D-array vervalt tot een aanwijzer die naar de eerste rij wijst. Het functie-prototype zou dus moeten zijn –

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

Ik geef de voorkeur aan std::vectorboven onbewerkte arrays.


Antwoord 8, autoriteit 2%

Hier is een voorbeeld van een vector van vectorenmatrix

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

uitvoer:

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

Antwoord 9

Je kunt zoiets als dit doen…

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

Uw uitvoer is als volgt …

11.5  12.5
13.5  14.5

Antwoord 10

We kunnen verschillende manieren gebruiken om een ​​2D-array door te geven aan een functie:

  • met behulp van enkele aanwijzer We moeten de 2D-array typen.

    #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;
     }
    
  • met dubbele wijzer Op deze manier typeren we ook de 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;
    }
    

Antwoord 11

Een belangrijk ding voor het doorgeven van multidimensionale arrays is:

  • First array dimensionhoeft niet te worden opgegeven.
  • Second(any any further)dimensionmoet worden opgegeven.

1.Wanneer alleen de tweede dimensie wereldwijd beschikbaar is (als macro of als globale constante)

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.Een enkele aanwijzer gebruiken:
Bij deze methode moeten we de 2D-array typeren bij het doorgeven aan functie.

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

Antwoord 12

U kunt hiervoor de sjabloonfaciliteit in C++ gebruiken. Ik deed zoiets als dit:

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

het probleem met deze benadering is dat voor elke waarde van col die u opgeeft, een nieuwe functiedefinitie wordt gemaakt met behulp van de sjabloon.
dus,

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

maakt de sjabloon twee keer aan om 2 functiedefinities te maken (een waarbij col = 3 en een waarbij col = 5).


Antwoord 13

Als je int a[2][3]wilt doorgeven aan void func(int** pp), heb je de volgende hulpstappen nodig.

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

Omdat de eerste [2]impliciet kan worden gespecificeerd, kan deze verder worden vereenvoudigd als.

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

Antwoord 14

In het geval dat u een dynamische 2-d array aan een functie wilt doorgeven, kan het gebruik van enkele aanwijzers voor u werken.

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

Antwoord 15

Je mag de meest linkse dimensie weglaten en dus krijg je twee opties:

void f1(double a[][2][3]) { ... }
void f2(double (*a)[2][3]) { ... }
double a[1][2][3];
f1(a); // ok
f2(a); // ok 

Dit is hetzelfde met pointers:

// compilation error: cannot convert ‘double (*)[2][3]’ to ‘double***’ 
// double ***p1 = a;
// compilation error: cannot convert ‘double (*)[2][3]’ to ‘double (**)[3]’
// double (**p2)[3] = a;
double (*p3)[2][3] = a; // ok
// compilation error: array of pointers != pointer to array
// double *p4[2][3] = a;
double (*p5)[3] = a[0]; // ok
double *p6 = a[0][1]; // ok

Het verval van een N-dimensionale array naar een pointer naar een N-1-dimensionale array is toegestaan door de C++-standaard, aangezien je de meest linkse dimensie kunt verliezen en toch correct toegang hebt tot array-elementen met N -1 dimensie informatie.

Details in hier

Hoewel arrays en pointers niet hetzelfde zijn: een array kan vervallen tot een pointer, maar een pointer heeft geen status over de grootte/configuratie van de gegevens waarnaar hij verwijst.

Een char **is een aanwijzer naar een geheugenblok met daarin tekenaanwijzers, die zelf naar geheugenblokken met tekens verwijzen. Een char [][]is een enkel geheugenblokdat tekens bevat. Dit heeft invloed op hoe de compiler de code vertaalt en hoe de uiteindelijke prestatie zal zijn.

Bron


Antwoord 16

Het zou heel eenvoudig zijn als in plaats van 2d array, vector<vector>wordt gebruikt.

Other episodes