Il y a des tonnes de questions similaires, mais je n'ai toujours pas trouvé de réponse pertinente pour la fonctionnalité des tableaux de longueur variable dans C99/C11.
Comment passer tableau multidimensionnel de longueur variable à une fonction en C99/C11?
Par exemple:
void foo(int n, int arr[][]) // <-- error here, how to fix?
{
}
void bar(int n)
{
int arr[n][n];
foo(n, arr);
}
Le compilateur (g++-4.7 -std=gnu++11
) Dit:error: declaration of ‘arr’ as multidimensional array must have bounds for all dimensions except the first
Si je le change en int *arr[]
, Le compilateur se plaint toujours:error: cannot convert ‘int (*)[(((sizetype)(((ssizetype)n) + -1)) + 1)]’ to ‘int**’ for argument ‘2’ to ‘void foo(int, int**)’
Question suivante, comment la transmettre par valeur et comment la transmettre par référence? Apparemment, vous ne voulez généralement pas que le tableau entier soit copié lorsque vous le passez à une fonction.
Avec des tableaux de longueur constante, c'est simple, car, comme l'indique la "constante", vous devez connaître la longueur lorsque vous déclarez la fonction:
void foo2(int n, int arr[][10]) // <-- ok
{
}
void bar2()
{
int arr[10][10];
foo2(10, arr);
}
Je sais, passer des tableaux à des fonctions comme celle-ci n'est pas une meilleure pratique, et je ne l'aime pas du tout. Il vaut probablement mieux faire avec des pointeurs plats, ou des objets (comme std: vector) ou autrement. Mais quand même, je suis un peu curieux de savoir quelle est la réponse ici d'un point de vue théorique.
Passer des tableaux à des fonctions est un peu drôle en C et C++. Il n'y a pas de valeur de type de tableau, vous passez donc un pointeur.
Pour adresser un tableau 2D (un vrai, pas un tableau de tableaux), vous devrez passer 2 blocs de données:
Et ce sont deux valeurs distinctes, que ce soit C ou C++ ou avec VLA ou sans ou autre chose.
Le plus simple, fonctionne partout mais nécessite plus de travail manuel
void foo(int width, int* arr) {
arr[x + y*width] = 5;
}
VLA, standard C99
void foo(int width, int arr[][width]) {
arr[x][y] = 5;
}
VLA avec arguments inversés, déclaration de paramètres avant (extension GNU C)
void foo(int width; int arr[][width], int width) {
arr[x][y]=5;
}
C++ w/VLA (extension GNU C++, terriblement moche)
void foo(int width, int* ptr) {
typedef int arrtype[][width];
arrtype& arr = *reinterpret_cast<arrtype*>(ptr);
arr[x][y]=5;
}
La notation [x] [y] avec un tableau 2D fonctionne car le type du tableau contient la largeur. Aucun type de tableau VLA = ne doit être corrigé au moment de la compilation.
Par conséquent: si vous ne pouvez pas utiliser VLA, alors ...
Si vous pouvez utiliser VLA (C99 ou GNU C++), alors ...
Pour C++, boost::multi_array
est un choix solide.
Pour les tableaux 2D, vous pouvez effectuer deux allocations distinctes:
T
(A)T
(B)Réglez ensuite les pointeurs dans (A) pour qu'ils pointent dans les rangées respectives de (B).
Avec cette configuration, vous pouvez simplement passer (A) comme un simple T**
et il se comportera bien avec [x][y]
indexation.
Cette solution est sympa pour la 2D, mais a besoin de plus en plus de passe-partout pour des dimensions plus élevées. Elle est également plus lente que la solution VLA en raison de la couche supplémentaire d'indirection.
Vous pouvez également exécuter une solution similaire avec une allocation distincte pour chaque ligne de B
. En C, cela ressemble à un malloc-in-a-loop, et est analogue au vecteur de vecteurs de C++ Cependant, cela enlève l'avantage d'avoir tout le tableau dans un seul bloc.
Il n'y a pas de méthode claire pour cela, mais vous pouvez utiliser une solution de contournement pour traiter un tableau bidimensionnel comme un tableau unidimensionnel, puis le reconvertir en un tableau bidimensionnel à l'intérieur de la fonction.
void foo2(int n, int *arr)
{
int *ptr; // use this as a marker to go to next block
int i;
int j;
for(i = 0; i < n; i++)
{
ptr = arr + i*n; // this is the starting for arr[i] ...
for (j = 0; j < n ;j++)
{
printf(" %d ", ptr[j]); // This is same as arr[i][j]
}
}
}
void bar2()
{
int arr[10][10];
foo2(10, (int *)arr);
}