Przekazywanie tablicy 2D do funkcji C++

Mam funkcję, którą chcę przyjąć jako parametr tablicę 2D o zmiennej wielkości.

Do tej pory mam to:

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

A ja zadeklarowałem tablicę gdzie indziej w moim kodzie:

double anArray[10][10];

Jednak wywołanie myFunction(anArray) daje mi błąd.

Nie chcę kopiować tablicy, kiedy ją przekazuję. Wszelkie zmiany wprowadzone w myFunction powinny zmienić stan anArray. Jeśli dobrze rozumiem, chcę tylko przekazać jako argument wskaźnik do tablicy 2D. Funkcja musi zaakceptować również tablice różnej wielkości. Na przykład [10][10] i [5][5]. Jak mogę to zrobić?

Author: Peter Mortensen, 2012-01-07

12 answers

Istnieją trzy sposoby przekazywania tablicy 2D do funkcji:

  1. Parametr jest tablicą 2D

    int array[10][10];
    void passFunc(int a[][10])
    {
        // ...
    }
    passFunc(array);
    
  2. Parametr jest tablicą zawierającą wskaźniki

    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. Parametr jest wskaźnikiem do wskaźnika

    int **array;
    array = new int *[10];
    for(int i = 0; i <10; i++)
        array[i] = new int[10];
    void passFunc(int **a)
    {
        // ...
    }
    passFunc(array);
    
 339
Author: shengy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-02-25 18:38:23

Stały Rozmiar

1. Pass by reference

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

W C++ przekazywanie tablicy przez referencję bez utraty informacji o wymiarach jest prawdopodobnie najbezpieczniejsze, ponieważ nie trzeba się martwić o to, że wywołujący przekazuje nieprawidłowy wymiar (flagi kompilatora podczas niedopasowania). Jednak nie jest to możliwe w przypadku tablic dynamicznych (freestore); działa to tylko dla tablic automatycznych (, Zwykle stosowych ), tzn. wymiarowość powinna być znana podczas kompilacji czas.

2. Pass by pointer

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

Odpowiednikiem C poprzedniej metody jest przekazanie tablicy wskaźnikiem. Nie należy tego mylić z przekazywaniem przez rozkładany typ wskaźnika tablicy(3), co jest powszechną, popularną metodą, choć mniej bezpieczną od tej, ale bardziej elastyczną. Jak (1), użyj tej metody, gdy wszystkie wymiary tablicy są stałe i znane w czasie kompilacji. Zauważ, że podczas wywoływania funkcji adres tablicy powinien jest przekazywany process_2d_array_pointer(&a), a nie adres pierwszego elementu przez decay process_2d_array_pointer(a).

Zmienna Wielkość

Są one dziedziczone po C, ale są mniej bezpieczne, kompilator nie ma możliwości sprawdzenia, gwarantując, że wywołujący przekazuje wymagane wymiary. Funkcja działa tylko na to, co wywołujący przekazuje jako wymiar (- Y). Są one bardziej elastyczne niż powyższe, ponieważ tablice o różnej długości mogą być do nich przekazywane niezmiennie.

Należy pamiętać, że nie ma takie coś jak przekazanie tablicy bezpośrednio do funkcji w C [podczas gdy w C++ mogą być przekazywane jako referencja (1)]; (2) przekazuje wskaźnik do tablicy, a nie samą tablicę. Zawsze przekazywanie tablicy jako-is staje się operacją kopiowania wskaźnika, co jest ułatwione przez naturę rozkładanie tablicy do wskaźnika .

3. Przekaż (wartość) wskaźnik do typu decayed

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

Chociaż int array[][10] jest dozwolone, nie polecam go nad powyższa składnia ponieważ powyższa składnia wyjaśnia, że identyfikator array jest pojedynczym wskaźnikiem do tablicy 10 liczb całkowitych, podczas gdy ta składnia wygląda jak tablica 2D, ale jest tym samym wskaźnikiem do tablicy 10 liczb całkowitych. Tutaj znamy liczbę elementów w jednym wierszu (tj. rozmiar kolumny, 10 tutaj), ale liczba wierszy jest nieznana i dlatego ma być przekazywana jako argument. W tym przypadku istnieje pewne bezpieczeństwo, ponieważ kompilator może oznaczać, gdy wskaźnik do tablicy z drugim przekazywany jest wymiar nie równy 10. Pierwszy wymiar jest częścią zmienną i można go pominąć. zobacz tutaj uzasadnienie dlaczego tylko pierwszy wymiar może być pominięty.

4. Przekaż wskaźnik do wskaźnika

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

Ponownie istnieje alternatywna składnia int *array[10], która jest taka sama jak int **array. W tej składni {[11] } jest ignorowany, ponieważ rozpada się na wskaźnik, stając się int **array. Być może jest to tylko sygnał do rozmówcy, że przekazana tablica powinien mieć co najmniej 10 kolumn, nawet wtedy wymagana jest liczba wierszy. W każdym razie kompilator nie zaznacza żadnych naruszeń Długości/rozmiaru (sprawdza tylko, czy przekazywany typ jest wskaźnikiem do wskaźnika), stąd Wymaganie zarówno liczenia wierszy, jak i kolumn jako parametr ma tu sens.

Uwaga: (4) jest najmniej najbezpieczniejszą opcją, ponieważ prawie nie ma kontroli typu i jest najbardziej niewygodna. Nie można legalnie przekazać tablicy 2D do tej funkcji; C-FAQ potępia zwykłe obejście działania int x[5][10]; process_pointer_2_pointer((int**)&x[0][0], 5, 10);, ponieważ może potencjalnie prowadzić do niezdefiniowanego zachowania ze względu na spłaszczenie tablicy. Właściwy sposób przekazania tablicy w tej metodzie sprowadza nas do niewygodnej części, tj. potrzebujemy dodatkowej tablicy (zastępczej) wskaźników z każdym jej elementem wskazującym odpowiedni wiersz rzeczywistej, która ma zostać przekazana; ta zastępcza tablica jest następnie przekazywana do funkcji (patrz poniżej); wszystko to dla wykonania tego samego zadania, co powyższe metody, które są bezpieczniejsze, czystsze i być może szybsze.

Oto program sterownika do testowania powyższych funkcji:

#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**
}
 132
Author: legends2k,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:02:56

Modyfikacja do pierwszej sugestii shengy ' ego, można użyć szablonów, aby funkcja zaakceptowała wielowymiarową zmienną tablicy (zamiast przechowywania tablicy wskaźników, które muszą być zarządzane i usuwane):

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

Instrukcje print są tam, aby pokazać, że tablice są przekazywane przez odniesienie (wyświetlając adresy zmiennych)

 34
Author: Zrax,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-07-27 19:21:04

Możesz utworzyć szablon funkcji w następujący sposób:

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

Wtedy masz oba rozmiary przez R i C. dla każdego rozmiaru tablicy zostanie utworzona inna funkcja, więc jeśli twoja funkcja jest duża i wywołasz ją z różnymi rozmiarami tablicy, może to być kosztowne. Możesz go użyć jako wrappera nad taką funkcją:

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

Traktuje tablicę jako jednowymiarową i używa arytmetyki do obliczania przesunięć indeksów. W takim przypadku zdefiniuj szablon tak:

template<int C, int R>
void myFunction(double (&myArray)[R][C])
{
    myFunction(*myArray, R, C);
}
 19
Author: Benjamin Lindley,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-01-07 04:20:20

anArray[10][10] nie jest wskaźnikiem do wskaźnika, jest to ciągła część pamięci odpowiednia do przechowywania 100 wartości typu double, którą kompilator wie, jak adresować, ponieważ określiłeś wymiary. Musisz przekazać go do funkcji jako tablicę. Możesz pominąć rozmiar początkowego wymiaru, w następujący sposób:

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

Nie pozwoli to jednak na przekazanie tablic o ostatnim wymiarze innym niż dziesięć.

Najlepszym rozwiązaniem w C++ jest użycie std::vector<std::vector<double> >: jest prawie tak samo wydajny i znacznie wygodniejsze.

 10
Author: dasblinkenlight,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-01-07 03:54:05

Dziwne, że nikt jeszcze o tym nie wspomniał, ale można po prostu szablon na wszystko 2D wspierające [][] semantyki.

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

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

Działa z dowolną strukturą danych 2D "podobną do tablicy", taką jak std::vector<std::vector<T>>, lub typem zdefiniowanym przez użytkownika, aby zmaksymalizować ponowne użycie kodu.

 9
Author: LemonPi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-12-22 18:26:25

Tablica jednowymiarowa rozpada się na wskaźnik wskazujący na pierwszy element tablicy. Podczas gdy tablica 2D rozpada się na wskaźnik wskazujący na pierwszy wiersz. Tak więc, prototyp funkcji powinien być -

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

Wolałbym std::vector niż surowe tablice.

 7
Author: Mahesh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-01-07 03:54:54

Możesz zrobić coś takiego...

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

Twój wynik będzie następujący...

11.5  12.5
13.5  14.5
 7
Author: Sagar Shah,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-20 14:57:13

Ważną rzeczą przy przekazywaniu tablic wielowymiarowych jest:

  • First array dimension nie muszą być określone.
  • Second(any any further)dimension musi być określone.

1.Gdy tylko drugi wymiar jest dostępny globalnie (jako makro lub jako stała globalna)

`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.Użycie pojedynczego wskaźnika : W tej metodzie musimy wpisać tablicę 2D podczas przechodzenia do funkcji.

`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
Author: sonorous,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-10 04:41:29

Oto wektor macierzy wektorów przykład

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

Wyjście:

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

0 0 0 0
0 0 0 0
0 0 0 0
 1
Author: edW,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-07 19:21:20

Możesz użyć do tego obiektu template w C++. Zrobiłem coś takiego:

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

Problem z tym podejściem polega na tym, że dla każdej wartości col, którą podajesz, nowa definicja funkcji jest tworzona za pomocą szablonu. więc

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

Tworzy instancję szablonu dwukrotnie, aby uzyskać 2 Definicje funkcji (jedna gdzie col = 3 i jedna gdzie col = 5).

 0
Author: vantony,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-22 18:51:21

Możemy użyć kilku sposobów, aby przekazać tablicę 2D do funkcji:

  • Używając pojedynczego wskaźnika musimy wpisać tablicę 2D.

    #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;
    }
    
  • Używając podwójnego wskaźnika w ten sposób wpisujemy również tablicę 2d

    #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;
   }
 0
Author: rashedcs,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-20 11:51:54