Passando array con dimensioni sconosciute per funzionare

Diciamo che ho una funzione chiamata MyFunction(int myArray[][]) che esegue alcune manipolazioni di array.

Se scrivo la lista dei parametri in questo modo, il compilatore si lamenterà che è necessario conoscere la dimensione dell’array in fase di compilazione. C’è un modo per riscrivere l’elenco dei parametri in modo che possa passare un array con qualsiasi dimensione alla funzione?

La dimensione del mio array è definita da due static const int MyFunction(int myArray[Board::ROWS][Board::COLS]) static const int in una class, ma il compilatore non accetterà qualcosa come MyFunction(int myArray[Board::ROWS][Board::COLS]) .

Cosa succede se potessi convertire l’array in un vettore e quindi passare il vettore a MyFunction ? C’è una conversione di una riga che posso utilizzare o devo effettuare manualmente la conversione?

Nel linguaggio C ++, le dichiarazioni di array multidimensionali devono sempre includere tutte le dimensioni, ad eccezione forse del primo. Quindi, quello che stai cercando di fare non è ansible. Non è ansible dichiarare un parametro del tipo di array multidimensionale incorporato senza specificare esplicitamente le dimensioni.

Se è necessario passare a una funzione un array multidimensionale di dimensioni in fase di esecuzione, è ansible dimenticare l’utilizzo del tipo di array multidimensionale incorporato. Una ansible soluzione è utilizzare un array multidimensionale “simulato” (array 1D di puntatori ad altri array 1D o un semplice array 1D che simula array multidimensionali tramite il ricalcolo dell’indice).

In C ++ usa std :: vector per modellare gli array a meno che tu non abbia una ragione specifica per usare un array.

Esempio di un vettore 3×2 riempito con 0 chiamato “myArray” inizializzato:

 vector< vector > myArray(3, vector(2,0)); 

Passare questo costrutto è banale, e non è necessario andare in giro con il passare della lunghezza (perché tiene traccia):

 void myFunction(vector< vector > &myArray) { for(size_t x = 0;x < myArray.length();++x){ for(size_t y = 0;y < myArray[x].length();++y){ cout << myArray[x][y] << " "; } cout << endl; } } 

In alternativa puoi scorrere su iteratore:

 void myFunction(vector< vector > &myArray) { for(vector< vector >::iterator x = myArray.begin();x != myArray.end();++x){ for(vector::iterator y = x->begin();y != x->end();++y){ cout << *y << " "; } cout << endl; } } 

In C ++ 0x è ansible utilizzare la parola chiave auto per ripulire la soluzione iteratore vettoriale:

 void myFunction(vector< vector > &myArray) { for(auto x = myArray.begin();x != myArray.end();++x){ for(auto y = x->begin();y != x->end();++y){ cout << *y << " "; } cout << endl; } } 

E in c ++ 0x for_each diventa vitale con lambda

 void myFunction(vector< vector > &myArray) { for_each(myArray.begin(), myArray.end(), [](const vector &x){ for_each(x->begin(), x->end(), [](int value){ cout << value << " "; }); cout << endl; }); } 

O un intervallo basato su loop in c ++ 0x:

 void myFunction(vector< vector > &myArray) { for(auto x : myArray){ for(auto y : *x){ cout << *y << " "; } cout << endl; } } 

* Non sono vicino a un compilatore in questo momento e non ho testato questi, per favore sentiti libero di correggere i miei esempi.


Se conosci la dimensione dell'array in fase di compilazione, puoi fare quanto segue (supponendo che la dimensione sia [x] [10]):

 MyFunction(int myArray[][10]) 

Se è necessario passare una matrice di lunghezza variabile (allocata dynamicmente o possibilmente solo una funzione che richiede dimensioni diverse di array), è necessario gestire i puntatori .

E come i commenti a questa risposta dichiarano:

boost :: multiarray può essere appropriato poiché modella in modo più efficiente un array multidimensionale. Un vettore di vettori può avere implicazioni sulle prestazioni nel codice del percorso critico, ma nei casi tipici non si noterà probabilmente un problema.

Passalo come puntatore e prendi le dimensioni come argomento.

 void foo(int *array, int width, int height) { // initialize xPos and yPos assert(xPos >= 0 && xPos < width); assert(yPos >= 0 && yPos < height); int value = array[yPos * width + xPos]; } 

Questo presuppone che tu abbia un semplice array bidimensionale, come int x[50][50] .

Esistono già un insieme di risposte con la maggior parte dei suggerimenti comuni: usando std::vector , implementando una class matrix , fornendo la dimensione dell’array nell’argomento function … Sto solo aggiungendo un’altra soluzione basata su array nativi – nota che se ansible dovresti usare un’astrazione di livello superiore.

In ogni caso:

 template  void function( int (&array)[rows][cols] ) { // ... } 

Questa soluzione utilizza un riferimento all’array (si noti il ​​punto & e l’insieme delle parentesi attorno array ) invece di utilizzare la syntax del valore pass-by. Questo costringe il compilatore a non decadere l’array in un puntatore. Quindi le due dimensioni (che potrebbero essere state fornite come costanti di tempo di compilazione possono essere definite come argomenti del modello e il compilatore dedurrà le dimensioni per te.

NOTA: nella domanda si dice che le dimensioni sono in realtà delle costanti statiche dovresti essere in grado di usarle nella firma della funzione se fornisci il valore nella dichiarazione della class:

 struct test { static const int rows = 25; static const int cols = 80; }; void function( int *array[80], int rows ) { // ... } 

Si noti che nella firma preferisco modificare l’array a doppia dimensione per un puntatore a un array. La ragione è che questo è ciò che il compilatore interpreta in entrambi i modi, e in questo modo è chiaro che non c’è alcuna garanzia che il chiamante della funzione passi una matrice di esattamente 25 linee (il compilatore non la applicherà), ed è quindi è evidente la necessità del secondo argomento intero in cui il chiamante supera il numero di righe.

Non puoi passare una dimensione arbitraria come quella; il compilatore non sa come generare l’aritmetica del puntatore. Potresti fare qualcosa come:

 MyFunction(int myArray[][N]) 

o potresti fare:

 MyFunction(int *p, int M, int N) 

ma dovrai prendere l’indirizzo del primo elemento quando lo chiami (cioè MyFunction(&arr[0][0], M, N) .

Puoi aggirare tutti questi problemi in C ++ usando una class contenitore; std::vector sarebbe un buon punto di partenza.

Il compilatore si lamenta perché deve conoscere la dimensione del tutto tranne la prima dimensione per poter indirizzare un elemento nell’array. Ad esempio, nel seguente codice:

 int array[M][N]; // ... array[i][j] = 0; 

Per indirizzare l’elemento, il compilatore genera qualcosa di simile al seguente:

 *(array+(i*N+j)) = 0; 

Pertanto, devi riscrivere la tua firma in questo modo:

 MyFunction(int array[][N]) 

nel qual caso sarai bloccato con una dimensione fissa, o andrai con una soluzione più generale come una class di matrice 2D dynamic (personalizzata) o un vector > .

  1. Usa un vector > (questo sarebbe un inganno se non fosse garantito che lo storage sottostante fosse contiguo).

  2. Utilizzare un puntatore al element-of-array ( int* ) e alla size ( M*N ). Ecco i draghi.

Innanzitutto, vediamo perché il compilatore si lamenta.

Se un array è definito come int arr[ ROWS ][ COLS ]; quindi qualsiasi notazione dell’array arr[ i ][ j ] può essere convertita nella notazione del puntatore come

 *( arr + i * COLS + j ) 

Osserva che l’espressione richiede solo COLS , non richiede ROWS . Quindi, la definizione dell’array può essere scritta in modo equivalente a

 int arr [][ COLS ]; 

Ma la mancanza della seconda dimensione non è accettabile . Per ulteriori dettagli, leggi qui .

Ora, sulla tua domanda:

C’è un modo per riscrivere l’elenco dei parametri in modo che possa passare un array con qualsiasi dimensione alla funzione?

Sì, forse puoi usare un puntatore, ad es. MyFunction( int * arr ); . Ma, pensateci, come potrebbe MyFunction() sapere dove smettere di accedere alla matrice? Per risolvere il problema è necessario un altro parametro per la lunghezza dell’array, ad esempio MyFunction( int * arr, size_t arrSize );

Sì: MyFunction(int **myArray);

Attento, però. Faresti meglio a sapere cosa stai facendo. Questo accetterà solo un array di puntatori int.

Poiché stai provando a passare una matrice di array, avrai bisogno di un’espressione costante come una delle dimensioni:

MyFunction(int myArray[][COLS]);

Avrai bisogno di avere COLS al momento della compilazione.

Suggerisco invece di usare un vettore .

Passa un puntatore e fai l’indicizzazione tu stesso o usa invece una class Matrix.

si, basta passarlo come puntatore / i:

 MyFunction(int** someArray) 

Il rovescio della medaglia è che probabilmente avrai bisogno di passare anche le lunghezze dell’array

Usa MyFunction(int *myArray[])
Se si utilizza MyFunction(int **myArray) un pass int someArray[X][Y] , il programma si bloccherà.
EDIT: non utilizzare la prima riga, è spiegato nei commenti.

Non conosco il C ++, ma lo standard C99 ha introdotto matrici di lunghezza variabile.

Quindi questo funzionerebbe in un compilatore che supporta C99:

 void func(int rows, int cols, double[rows][cols] matrix) { for (int r = 0; r < rows; r++) { for (int c = 0; c < cols; c++) { printf("%f", matrix[r][c]); } } } 

Si noti che gli argomenti delle dimensioni vengono prima dell'array. In realtà, solo il numero di colonne deve essere noto al momento della compilazione, quindi anche questo sarebbe valido:

 void func(int rows, int cols, double[][cols] matrix) 

Per tre o più dimensioni, tutte tranne la prima dimensione devono avere dimensioni note. La risposta ArunSaha collegata spiega perché.

Onestamente, non so se C ++ supporti array di lunghezza variabile, quindi questo può o non può funzionare. In entrambi i casi, si può anche considerare di incapsulare l'array in una sorta di class matrix.

EDIT: dalla tua modifica, sembra che C ++ potrebbe non supportare questa funzione. Una class matrice è probabilmente la strada da percorrere. (Oppure std :: vector se non ti dispiace che la memoria non possa essere assegnata in modo contiguo.)

Non passare un array, che è un dettaglio di implementazione. Passa il Consiglio

 MyFunction(Board theBoard) { ... } 

In C ++ 0x, puoi usare std::initializer_list<...> per realizzare questo:

 MyFunction(std::initializer_list> myArray); 

e usarlo (presumo) in questo modo (con l’ intervallo basato sulla syntax ):

 for (const std::initializer_list &subArray: myArray) { for (int value: subArray) { // fun with value! } } 

in realtà la dimensione del mio array è definita da due static const int in una class, ma il compilatore non accetterà qualcosa come MyFunction(int myArray[Board::ROWS][Board::COLS]) .

È strano, funziona perfettamente bene per me:

 struct Board { static const int ROWS = 6; static const int COLS = 7; }; void MyFunction(int myArray[Board::ROWS][Board::COLS]) { } 

Forse ROWS e COLS sono privati? Puoi mostrarci un codice?

In C ++, l’utilizzo dei tipi di array incorporati è un errore immediato. Potresti usare boost :: / std :: array di array o vettore di array. Gli array primitivi non sono adatti ad alcun tipo di uso reale