J'ai une fonction que je veux prendre, en paramètre, un tableau 2D de taille variable.
Jusqu'à présent, j'ai ceci:
void myFunction(double** myArray){
myArray[x][y] = 5;
etc...
}
Et j'ai déclaré un tableau ailleurs dans mon code:
double anArray[10][10];
Cependant, appeler myFunction(anArray)
me donne une erreur.
Je ne veux pas copier le tableau lorsque je le passe. Toute modification apportée à myFunction
devrait modifier l'état de anArray
. Si je comprends bien, je veux seulement passer en argument un pointeur vers un tableau 2D. La fonction doit également accepter des tableaux de différentes tailles. Ainsi, par exemple, [10][10]
et [5][5]
. Comment puis-je faire ceci?
c++
arrays
pointers
multidimensional-array
RogerDarwin
la source
la source
func(int* mat, int r, int c){ for(int i=0; i<r; i++) for(int j=0; j<c; j++) printf("%d ", *(mat+i*c+j)); }
. Appelez-le commeint mat[3][5]; func(mat[0], 3, 5);
Réponses:
Il existe trois façons de passer un tableau 2D à une fonction:
Le paramètre est un tableau 2D
Le paramètre est un tableau contenant des pointeurs
Le paramètre est un pointeur sur un pointeur
la source
array
avecarray[i][j]
:)int (*a)[10]
.int **
.int (*a) [10]
.Taille fixe
1. Passer par référence
En C ++, passer le tableau par référence sans perdre les informations de dimension est probablement le plus sûr, car il n'est pas nécessaire de se soucier que l'appelant passe une dimension incorrecte (drapeaux du compilateur en cas de non-correspondance). Cependant, cela n'est pas possible avec les tableaux dynamiques (magasin libre); cela ne fonctionne que pour les tableaux automatiques ( généralement empilés ), c'est-à-dire que la dimensionnalité doit être connue au moment de la compilation.
2. Passer par le pointeur
L'équivalent C de la méthode précédente passe le tableau par pointeur. Cela ne doit pas être confondu avec le passage par le type de pointeur pourri du tableau (3) , qui est la méthode courante et populaire, bien que moins sûre que celle-ci mais plus flexible. Comme (1) , utilisez cette méthode lorsque toutes les dimensions du tableau sont fixes et connues au moment de la compilation. Notez que lors de l'appel de la fonction, l'adresse du tableau doit être transmise
process_2d_array_pointer(&a)
et non l'adresse du premier élément par décroissanceprocess_2d_array_pointer(a)
.Taille variable
Celles-ci sont héritées de C mais sont moins sûres, le compilateur n'a aucun moyen de vérifier, garantissant que l'appelant passe les dimensions requises. La fonction ne dépend que de ce que l'appelant passe comme dimension (s). Celles-ci sont plus flexibles que les précédentes, car des tableaux de différentes longueurs peuvent leur être transmis invariablement.
Il faut se rappeler qu'il n'y a rien de tel que de passer un tableau directement à une fonction en C [tandis qu'en C ++, ils peuvent être passés comme référence (1) ]; (2) transmet un pointeur au tableau et non au tableau lui-même. Toujours passer un tableau tel quel devient une opération de copie de pointeur qui est facilitée par la nature du tableau de se désintégrer en un pointeur .
3. Passez (valeur) un pointeur sur le type pourri
Bien que cela
int array[][10]
soit autorisé, je ne le recommanderais pas par-dessus la syntaxe ci-dessus, car la syntaxe ci-dessus indique clairement que l'identifiantarray
est un pointeur unique vers un tableau de 10 entiers, tandis que cette syntaxe ressemble à un tableau 2D mais est le même pointeur vers un tableau de 10 entiers. Ici, nous connaissons le nombre d'éléments dans une seule ligne (c'est-à-dire la taille de la colonne, 10 ici) mais le nombre de lignes est inconnu et doit donc être passé en argument. Dans ce cas, il y a une certaine sécurité car le compilateur peut signaler lorsqu'un pointeur vers un tableau avec une deuxième dimension non égale à 10 est passé. La première dimension est la partie variable et peut être omise. Voir ici pour la raison pour laquelle seule la première dimension peut être omise.4. Passer un pointeur à un pointeur
Encore une fois, il existe une syntaxe alternative
int *array[10]
qui est la même queint **array
. Dans cette syntaxe, le[10]
est ignoré car il se désintègre en un pointeur devenant ainsiint **array
. C'est peut-être juste un signal à l'appelant que le tableau passé doit avoir au moins 10 colonnes, même si le nombre de lignes est requis. Dans tous les cas, le compilateur ne signale aucune violation de longueur / taille (il vérifie uniquement si le type transmis est un pointeur vers un pointeur), nécessitant donc à la fois le nombre de lignes et de colonnes comme paramètre est logique ici.Remarque: (4) est l'option la moins sûre car elle ne comporte quasiment aucun contrôle de type et est la plus gênante. On ne peut légitimement passer un tableau 2D à cette fonction; C-FAQ condamne la solution de contournement habituelle
int x[5][10]; process_pointer_2_pointer((int**)&x[0][0], 5, 10);
car cela peut potentiellement conduire à un comportement indéfini en raison de l'aplatissement de la baie. La bonne façon de passer un tableau dans cette méthode nous amène à la partie gênante, c'est-à-dire que nous avons besoin d'un tableau supplémentaire (de substitution) de pointeurs avec chacun de ses éléments pointant vers la ligne respective du tableau réel à passer; ce substitut est ensuite transmis à la fonction (voir ci-dessous); tout cela pour faire le même travail que les méthodes ci-dessus qui sont plus sûres, plus propres et peut-être plus rapides.Voici un programme pilote pour tester les fonctions ci-dessus:
la source
b[i] = a[i];
, disons,b[i] = new int[10];
. On peut également faireb
alloué dynamiquementint **b = int *[5];
et cela fonctionnera toujours tel quel.array[i][j]
fonctionne-t-il dans la fonction en 4) ? Parce qu'il a reçu ptr en ptr et ne connaît pas la valeur de la dernière dimension, qui est nécessaire pour effectuer un décalage pour un adressage correct?array[i][j]
est simplement l'arithmétique du pointeur, c'est-à-dire à la valeur du pointeurarray
, il ajouteraiti
et déréférencerait le résultat commeint*
, auquel il ajouteraitj
et déréférencerait cet emplacement, en lisant unint
. Donc, non, il n'a pas besoin de connaître de dimension pour cela. Mais c'est tout l'intérêt! Le compilateur prend la parole du programmeur avec foi et si le programmeur était incorrect, un comportement indéfini s'ensuit. C'est la raison pour laquelle j'ai mentionné que le cas 4 est l'option la moins sûre.Une modification de la première suggestion de shengy, vous pouvez utiliser des modèles pour que la fonction accepte une variable de tableau multidimensionnel (au lieu de stocker un tableau de pointeurs qui doivent être gérés et supprimés):
Les instructions print sont là pour montrer que les tableaux sont passés par référence (en affichant les adresses des variables)
la source
%p
pour imprimer un pointeur, et même dans ce cas, vous devez le convertir envoid *
, sinon,printf()
invoque un comportement non défini. De plus, vous ne devez pas utiliser l'&
opérateur addressof ( ) lors de l'appel des fonctions, car les fonctions attendent un argument de typedouble (*)[size_y]
, alors que vous leur passez actuellementdouble (*)[10][10]
etdouble (*)[5][5]
.Surpris que personne ne l'ait encore mentionné, mais vous pouvez simplement créer un modèle sur tout ce qui prend en charge la sémantique [] [] 2D.
Il fonctionne avec n'importe quelle infrastructure de données 2D de type tableau, telle que
std::vector<std::vector<T>>
, ou un type défini par l'utilisateur pour maximiser la réutilisation du code.la source
Vous pouvez créer un modèle de fonction comme celui-ci:
Ensuite, vous avez les deux tailles de dimension via R et C. Une fonction différente sera créée pour chaque taille de tableau, donc si votre fonction est grande et que vous l'appelez avec une variété de tailles de tableau différentes, cela peut être coûteux. Vous pouvez cependant l'utiliser comme wrapper sur une fonction comme celle-ci:
Il traite le tableau comme unidimensionnel et utilise l'arithmétique pour déterminer les décalages des index. Dans ce cas, vous définiriez le modèle comme ceci:
la source
size_t
est le meilleur type pour les index de tableau queint
.anArray[10][10]
n'est pas un pointeur vers un pointeur, c'est un morceau de mémoire contigu approprié pour stocker 100 valeurs de type double, que le compilateur sait comment traiter car vous avez spécifié les dimensions. Vous devez le passer à une fonction sous forme de tableau. Vous pouvez omettre la taille de la dimension initiale, comme suit:Cependant, cela ne vous permettra pas de passer des tableaux avec la dernière dimension autre que dix.
La meilleure solution en C ++ est d'utiliser
std::vector<std::vector<double> >
: elle est presque aussi efficace et nettement plus pratique.la source
Le tableau unidimensionnel se désintègre en un pointeur pointeur pointant vers le premier élément du tableau. Alors qu'un tableau 2D se désintègre en un pointeur pointant vers la première ligne. Ainsi, le prototype de fonction devrait être -
Je préfère
std::vector
les tableaux bruts.la source
Vous pouvez faire quelque chose comme ça ...
Votre sortie sera la suivante ...
la source
Voici un exemple de matrice de vecteurs
production:
la source
Nous pouvons utiliser plusieurs façons de passer un tableau 2D à une fonction:
En utilisant un seul pointeur, nous devons transtyper le tableau 2D.
Utilisation du pointeur double De cette façon, nous avons également transtypé le tableau 2D
la source
Une chose importante pour passer des tableaux multidimensionnels est:
First array dimension
n'a pas besoin d'être spécifié.Second(any any further)dimension
doit être spécifié.1.Lorsque seule la deuxième dimension est disponible à l'échelle mondiale (sous forme de macro ou de constante globale)
2.Utilisation d'un seul pointeur : Dans cette méthode, nous devons transtyper le tableau 2D lors du passage à la fonction.
la source
Vous pouvez utiliser la fonction de modèle en C ++ pour ce faire. J'ai fait quelque chose comme ça:
le problème avec cette approche est que pour chaque valeur de col que vous fournissez, la nouvelle définition d'une fonction est instanciée à l'aide du modèle. alors,
instancie le modèle deux fois pour produire 2 définitions de fonction (une où col = 3 et une où col = 5).
la source
Si vous voulez passer
int a[2][3]
àvoid func(int** pp)
vous avez besoin d'étapes auxiliaires comme suit.Comme le premier
[2]
peut être implicitement spécifié, il peut être encore simplifié au fur et à mesure.la source
Dans le cas où vous souhaitez passer un tableau 2D de taille dynamique à une fonction, l'utilisation de certains pointeurs pourrait fonctionner pour vous.
la source
Vous êtes autorisé à omettre la dimension la plus à gauche et vous vous retrouvez donc avec deux options:
C'est la même chose avec les pointeurs:
La désintégration d'un tableau à N dimensions vers un pointeur vers un tableau à N-1 dimensions est autorisée par la norme C ++ , car vous pouvez perdre la dimension la plus à gauche et toujours pouvoir accéder correctement aux éléments du tableau avec les informations de dimension N-1.
Détails ici
Cependant, les tableaux et les pointeurs ne sont pas les mêmes : un tableau peut se désintégrer en un pointeur, mais un pointeur ne porte pas d'état sur la taille / configuration des données vers lesquelles il pointe.
A
char **
est un pointeur vers un bloc de mémoire contenant des pointeurs de caractères , qui pointent eux-mêmes vers des blocs de mémoire de caractères. Achar [][]
est un bloc de mémoire unique qui contient des caractères. Cela a un impact sur la façon dont le compilateur traduit le code et sur les performances finales.La source
la source