Comment déclarer un tableau 2D en utilisant new?
Par exemple, pour un tableau "normal", je voudrais:
int* ary = new int[Size]
mais
int** ary = new int[sizeY][sizeX]
a) ne fonctionne pas / ne compile pas et b) n'accomplit pas ce qui:
int ary[sizeY][sizeX]
Est-ce que.
Réponses:
Un tableau 2D dynamique est essentiellement un tableau de pointeurs vers des tableaux . Vous pouvez l'initialiser à l'aide d'une boucle, comme ceci:
Ce qui précède, pour
colCount= 5
etrowCount = 4
, produirait ce qui suit:la source
new
est créé sur le tas et doit être désalloué avecdelete
, gardez cela à l'esprit et assurez-vous de supprimer cette mémoire du tas lorsque vous en avez terminé pour éviter les fuites.T (*ptr)[M] = new T[N][M];
est la bonne solution… Aucune quantité de tableaux de pointeurs ne sera jamais la même qu'un tableau de tableaux…devrait être:
puis nettoyer serait:
EDIT: comme Dietrich Epp l'a souligné dans les commentaires, ce n'est pas exactement une solution légère. Une approche alternative serait d'utiliser un grand bloc de mémoire:
la source
i*sizeX+j
? Si je me souviens bien, avec l'ordre des lignes principales, il devrait s'agir de la ligne * numColumns + col.Bien que cette réponse populaire vous fournisse la syntaxe d'indexation souhaitée, elle est doublement inefficace: grande et lente à la fois dans l'espace et dans le temps. Il y a une meilleure façon.
Pourquoi cette réponse est grande et lente
La solution proposée consiste à créer un tableau dynamique de pointeurs, puis à initialiser chaque pointeur sur son propre tableau dynamique indépendant. L' avantage de cette approche est qu'elle vous donne la syntaxe d'indexation à laquelle vous êtes habitué, donc si vous voulez trouver la valeur de la matrice à la position x, y, vous dites:
Cela fonctionne car la matrice [x] renvoie un pointeur vers un tableau, qui est ensuite indexé avec [y]. Décomposer:
Pratique, oui? Nous aimons notre syntaxe [x] [y].
Mais la solution a un gros inconvénient , c'est qu'elle est à la fois grasse et lente.
Pourquoi?
La raison pour laquelle il est à la fois gras et lent est en fait la même. Chaque "ligne" de la matrice est un tableau dynamique alloué séparément. Faire une allocation de tas coûte cher en temps et en espace. L'allocateur prend du temps pour effectuer l'allocation, exécutant parfois des algorithmes O (n) pour le faire. Et l'allocateur "remplit" chacun de vos tableaux de lignes avec des octets supplémentaires pour la comptabilité et l'alignement. Cet espace supplémentaire coûte ... eh bien ... de l'espace supplémentaire. Le désallocateur également plus de temps lorsque vous allez désallouer la matrice, libérant soigneusement chaque allocation de ligne individuelle. Me fait transpirer juste en y pensant.
Il y a une autre raison pour laquelle c'est lent. Ces allocations distinctes ont tendance à vivre dans des parties discontinues de la mémoire. Une ligne peut être à l'adresse 1 000, une autre à l'adresse 100 000 - vous avez l'idée. Cela signifie que lorsque vous traversez la matrice, vous sautez dans la mémoire comme une personne sauvage. Cela a tendance à entraîner des échecs de cache qui ralentissent considérablement votre temps de traitement.
Donc, si vous devez absolument avoir votre jolie syntaxe d'indexation [x] [y], utilisez cette solution. Si vous voulez de la rapidité et de la petitesse (et si vous ne vous en souciez pas, pourquoi travaillez-vous en C ++?), Vous avez besoin d'une solution différente.
Une solution différente
La meilleure solution consiste à allouer votre matrice entière en un seul tableau dynamique, puis à utiliser vos propres calculs d'indexation (légèrement) intelligents pour accéder aux cellules. Les mathématiques d'indexation ne sont que très légèrement intelligentes; non, ce n'est pas intelligent du tout: c'est évident.
Compte tenu de cette
index()
fonction (que j'imagine être membre d'une classe car elle a besoin de connaître cellem_width
de votre matrice), vous pouvez accéder aux cellules de votre tableau matriciel. Le tableau matriciel est alloué comme ceci:Donc, l'équivalent de cela dans la solution lente et grasse:
... est-ce dans la petite solution rapide:
Triste, je sais. Mais vous vous y habituerez. Et votre CPU vous en remerciera.
la source
class Matrix { int* array; int m_width; public: Matrix( int w, int h ) : m_width( w ), array( new int[ w * h ] ) {} ~Matrix() { delete[] array; } int at( int x, int y ) const { return array[ index( x, y ) ]; } protected: int index( int x, int y ) const { return x + m_width * y; } };
ceci : si vous redressez ce code, cela pourrait avoir du sens et pourrait éclairer la réponse ci-dessus.#define ROW_COL_TO_INDEX(row, col, num_cols) (row*num_cols + col)
Ensuite, vous pouvez l'utiliser carint COLS = 4; A[ ROW_COL_TO_INDEX(r, c, COLS) ] = 75;
la surcharge affecte vraiment quand nous faisons des multiplications matricielles qui sont de complexité O (n ^ 3) ou O (n ^ 2.81) pour l'algorithme de Strassen .a[x][y]
, ce que vous faites réellement, c'est*(*(a + x) + y)
: deux ajouts et deux récupérations de mémoire. Aveca[index(x, y)]
, ce que vous faites réellement est*(a + x + w*y)
: deux ajouts, une multiplication et une extraction de mémoire. Ce dernier est souvent préférable, pour les raisons exposées dans cette réponse (c'est-à-dire que l'échange de la récupération de mémoire supplémentaire avec une multiplication en vaut la peine, en particulier parce que les données ne sont pas fragmentées et que vous ne devez donc pas manquer le cache).En C ++ 11, il est possible:
De cette façon, la mémoire n'est pas initialisée. Pour l'initialiser, procédez comme suit:
Exemple de programme (compiler avec "g ++ -std = c ++ 11"):
Production:
la source
using arr2d = double(*)[2]; arr2d array = new double[M][N];
double (*)[M][N]
oudouble(*)[][N]
avec M, N étant des expressions constantes.Je suppose à partir de votre exemple de tableau statique que vous voulez un tableau rectangulaire et non pas irrégulier. Vous pouvez utiliser les éléments suivants:
Ensuite, vous pouvez accéder aux éléments comme:
N'oubliez pas d'utiliser delete [] sur
ary
.la source
Il y a deux techniques générales que je recommanderais pour cela en C ++ 11 et supérieur, une pour les dimensions de temps de compilation et une pour le temps d'exécution. Les deux réponses supposent que vous voulez des tableaux bidimensionnels uniformes (pas des tableaux irréguliers).
Compiler les dimensions temporelles
Utilisez un
std::array
destd::array
puis utiliseznew
pour le mettre sur le tas:Encore une fois, cela ne fonctionne que si les tailles des dimensions sont connues au moment de la compilation.
Dimensions d'exécution
La meilleure façon d'accomplir un tableau bidimensionnel avec des tailles connues uniquement au moment de l'exécution est de l'envelopper dans une classe. La classe allouera un tableau 1d puis surchargera
operator []
pour fournir l'indexation de la première dimension. Cela fonctionne car en C ++, un tableau 2D est de ligne majeure:(Tiré de http://eli.thegreenplace.net/2015/memory-layout-of-multi-dimensional-arrays/ )
Une séquence de mémoire contiguë est bonne pour des raisons de performances et est également facile à nettoyer. Voici un exemple de classe qui omet de nombreuses méthodes utiles mais montre l'idée de base:
Nous créons donc un tableau avec des
std::make_unique<int[]>(rows * columns)
entrées. Nous surchargeonsoperator []
ce qui va indexer la ligne pour nous. Il renvoie unint *
qui pointe vers le début de la ligne, qui peut ensuite être déréférencé normalement pour la colonne. Notez quemake_unique
premier est livré en C ++ 14 mais vous pouvez le polyfill en C ++ 11 si nécessaire.Il est également courant que ces types de structures soient surchargées
operator()
:Techniquement , je ne l' ai pas utilisé
new
, mais il est trivial de passer d'std::unique_ptr<int[]>
àint *
et de l' utilisationnew
/delete
.la source
std::array
destd::array
s:std::array<std::array<int, columns> rows>
.asserts
des débogages pour vérifier les accès à la mémoire, etc. Ces ajouts rendent généralement le travail plus facile et plus agréable.make_unique
place denew/delete
.Cette question me dérangeait - c'est un problème assez courant pour qu'une bonne solution existe déjà, quelque chose de mieux que le vecteur de vecteurs ou le roulement de votre propre indexation de tableau.
Quand quelque chose devrait exister en C ++ mais n'existe pas, le premier endroit à regarder est boost.org . Là , je trouve la bibliothèque Boost Multidimensional Array,
multi_array
. Il inclut même unemulti_array_ref
classe qui peut être utilisée pour encapsuler votre propre tampon de tableau unidimensionnel.la source
auto
mot - clé. Je suis surpris qu'ils n'aient pas essayé de s'attaquer aux tableaux 2D, d'autant plus que Boost a déjà montré la voie.Pourquoi ne pas utiliser STL: vector? Si facile et vous n'avez pas besoin de supprimer le vecteur.
Vous pouvez également initialiser les «tableaux», il suffit de lui donner une valeur par défaut
Source: Comment créer des tableaux dimensionnels 2, 3 (ou multi) en C / C ++?
la source
Un tableau 2D est essentiellement un tableau 1D de pointeurs, où chaque pointeur pointe vers un tableau 1D, qui contiendra les données réelles.
Ici, N est une ligne et M est une colonne.
allocation dynamique
remplir
impression
gratuit
la source
Comment allouer un tableau multidimensionnel contigu dans GNU C ++? Il existe une extension GNU qui permet à la syntaxe "standard" de fonctionner.
Il semble que le problème vient de l'opérateur nouveau []. Assurez-vous d'utiliser plutôt l'opérateur new:
Et c'est tout: vous obtenez un tableau multidimensionnel compatible C ...
la source
double (*in)[m][n] = (double (*)[m][n])new double[k*m*n];
ne fonctionne pas non plus. Je reçois des erreurs C2057, C2540n
car elles ne sont pas connues au moment de la compilation. Je ne comprends pas pourquoi je ne peux pas le faire, car la mémoire est allouée correctement et ce ne sont que des pointeurs pour gérer cette mémoire de manière pratique. (VS 2010)gcc
m'a dupé quand j'ai écrit ceci: la fourniture-std=c++11
ne suffit pas pour activer la stricte conformité aux normes,-pedantic-errors
est également requise. Sans le dernier drapeau,gcc
accepte joyeusement la distribution, même si elle n'est en effet pas conforme à la norme C ++. Avec ce que je sais maintenant, je ne peux que conseiller de revenir à C lorsque vous faites des choses qui dépendent fortement de tableaux multidimensionnels. C99 est juste beaucoup plus puissant à cet égard que même C ++ 17 ne le sera.typedef est votre ami
Après être revenu en arrière et avoir examiné de nombreuses autres réponses, j'ai trouvé qu'une explication plus approfondie était en ordre, car la plupart des autres réponses souffrent de problèmes de performances ou vous obligent à utiliser une syntaxe inhabituelle ou contraignante pour déclarer le tableau ou accéder au tableau (ou tout ce qui précède).
Tout d'abord, cette réponse suppose que vous connaissez les dimensions du tableau au moment de la compilation. Si vous le faites, c'est la meilleure solution car elle donnera à la fois les meilleures performances et vous permettra d'utiliser la syntaxe de tableau standard pour accéder aux éléments du tableau .
La raison pour laquelle cela donne les meilleures performances est qu'il alloue tous les tableaux en tant que bloc de mémoire contigu, ce qui signifie que vous risquez d'avoir moins de sauts de page et une meilleure localité spatiale. L'allocation dans une boucle peut entraîner la dispersion des tableaux individuels sur plusieurs pages non contiguës à travers l'espace de mémoire virtuelle car la boucle d'allocation peut être interrompue (éventuellement plusieurs fois) par d'autres threads ou processus, ou simplement en raison de la discrétion du L'allocateur remplit de petits blocs de mémoire vides dont il dispose.
Les autres avantages sont une syntaxe de déclaration simple et une syntaxe d'accès aux tableaux standard.
En C ++ en utilisant new:
Ou style C en utilisant calloc:
la source
Ce problème me dérange depuis 15 ans et toutes les solutions apportées n'étaient pas satisfaisantes pour moi. Comment créer un tableau multidimensionnel dynamique contigu en mémoire? Aujourd'hui, j'ai enfin trouvé la réponse. En utilisant le code suivant, vous pouvez faire exactement cela:
Lorsque vous appelez le programme avec les valeurs sizeX = 20 et sizeY = 15, la sortie sera la suivante:
Comme vous pouvez le voir, le tableau multidimensionnel se trouve de manière contiguë dans la mémoire et il n'y a pas deux adresses mémoire qui se chevauchent. Même la routine de libération du tableau est plus simple que la méthode standard d'allocation dynamique de mémoire pour chaque colonne (ou ligne, selon la façon dont vous affichez le tableau). Puisque le tableau se compose essentiellement de deux tableaux linéaires, seuls ces deux doivent être (et peuvent être) libérés.
Cette méthode peut être étendue à plus de deux dimensions avec le même concept. Je ne le ferai pas ici, mais quand vous avez l'idée derrière cela, c'est une tâche simple.
J'espère que ce code vous aidera autant qu'il m'a aidé.
la source
array2d[i] = buffer + i * sizeX
. Cela aide donc dans une faible mesure, mais dans le code utilisant le tableau, le compilateur ne peut pas simplement incrémenter des pointeurs pour analyser le tableau.make_unique<int[]>(sizeX*sizeY)
pour configurer le stockage contigu etmake_unique<int*[]>(sizeX)
pour configurer le stockage des pointeurs (qui devraient être attribués de la même manière que vous le montrez). Cela vous libère de l'obligation d'appelerdelete[]
deux fois à la fin.temp
? Compte tenu des avantages (tableau continuos 2d avec des dimensions inconnues au moment de la compilation), je ne suis pas sûr que je me soucie de l'avoir pendant. Je n'ai pas compris ce que @PeterCordes veut direextra layer of indirection
, qu'est-ce que c'est? Pourquoi la parenthèse,array2d[i] = (temp + i * sizeX)
;Le but de cette réponse n'est pas d'ajouter quelque chose de nouveau que les autres ne couvrent pas déjà, mais d'étendre la réponse de @Kevin Loney.
Vous pouvez utiliser la déclaration légère:
et la syntaxe d'accès sera:
mais cela est lourd pour la plupart et peut entraîner de la confusion. Vous pouvez donc définir une macro comme suit:
Vous pouvez maintenant accéder au tableau en utilisant la syntaxe très similaire
ary(i, j) // means ary[i][j]
. Cela a l'avantage d'être simple et beau, et en même temps, utiliser des expressions à la place des indices est également plus simple et moins déroutant.Pour accéder, disons, à ary [2 + 5] [3 + 8], vous pouvez écrire à la
ary(2+5, 3+8)
place de l'aspect complexe, c'est-à-ary[(2+5)*SizeY + (3+8)]
dire qu'il enregistre les parenthèses et améliore la lisibilité.Mises en garde:
SizeY
doit être passé avec le même nom (ou à la place être déclaré comme variable globale).Ou, si vous devez utiliser le tableau dans plusieurs fonctions, vous pouvez également ajouter SizeY comme autre paramètre dans la définition de macro comme ceci:
Vous avez eu l'idée. Bien sûr, cela devient trop long pour être utile, mais cela peut encore éviter la confusion de + et *.
Ce n'est pas recommandé, et il sera condamné comme une mauvaise pratique par la plupart des utilisateurs expérimentés, mais je n'ai pas pu résister à le partager en raison de son élégance.
Modifier:
si vous voulez une solution portable qui fonctionne pour n'importe quel nombre de tableaux, vous pouvez utiliser cette syntaxe:
puis vous pouvez transmettre n'importe quel tableau à l'appel, avec n'importe quelle taille en utilisant la syntaxe d'accès:
PS: je les ai testés, et la même syntaxe fonctionne (en tant que lvalue et rvalue) sur les compilateurs g ++ 14 et g ++ 11.
la source
Essayez de faire ceci:
la source
Ici, j'ai deux options. Le premier montre le concept d'un tableau de tableaux ou pointeur de pointeurs. Je préfère le second car les adresses sont contiguës, comme vous pouvez le voir sur l'image.
la source
Si votre projet est CLI (Common Language Runtime Support) , alors:
Vous pouvez utiliser la classe tableau, pas celle que vous obtenez lorsque vous écrivez:
En d'autres termes, pas la classe de tableau non managée que vous obtenez lorsque vous utilisez l'espace de noms std et lorsque vous incluez l'en-tête de tableau, pas la classe de tableau non managée définie dans l'espace de noms std et dans l'en-tête de tableau, mais le tableau de classes gérées de la CLI.
avec cette classe, vous pouvez créer un tableau de n'importe quel rang .
Le code suivant ci-dessous crée un nouveau tableau bidimensionnel de 2 lignes et 3 colonnes et de type int, et je le nomme "arr":
Vous pouvez maintenant accéder aux éléments du tableau, par son nom et écrire une seule parenthèse au carré
[]
, et à l'intérieur, ajouter la ligne et la colonne et les séparer avec la virgule,
.Le code suivant ci-dessous accède à un élément dans la 2ème ligne et la 1ère colonne du tableau que j'ai déjà créé dans le code précédent ci-dessus:
écrire seulement cette ligne, c'est lire la valeur dans cette cellule, c'est-à-dire obtenir la valeur dans cette cellule, mais si vous ajoutez l'égalité
=
signe , vous êtes sur le point d'écrire la valeur dans cette cellule, c'est-à-dire de définir la valeur dans cette cellule. Vous pouvez également utiliser les opérateurs + =, - =, * = et / = bien sûr, uniquement pour les nombres (int, float, double, __int16, __int32, __int64 et etc.), mais vous le savez déjà.Si votre projet n'est pas CLI, vous pouvez utiliser la classe de tableau non managée de l'espace de noms std, si vous
#include <array>
, bien sûr, mais le problème est que cette classe de tableau est différente de celle du tableau CLI. La création d'un tableau de ce type est identique à l'interface CLI, sauf que vous devrez supprimer le^
signe et legcnew
mot - clé. Mais malheureusement, le deuxième paramètre int<>
entre parenthèses spécifie la longueur (c'est-à-dire la taille) du tableau, pas son rang!Il n'y a aucun moyen de spécifier le rang dans ce type de tableau, le rang est uniquement la fonction du tableau CLI . .
Le tableau std se comporte comme un tableau normal en c ++, que vous définissez avec un pointeur, par exemple
int*
, puis:,new int[size]
ou sans pointeur:,int arr[size]
mais contrairement au tableau normal du c ++, le tableau std fournit des fonctions que vous pouvez utiliser avec les éléments du tableau, comme fill, begin, end, size et etc, mais un tableau normal ne fournit rien .Mais les tableaux std sont toujours un tableau unidimensionnel, comme les tableaux c ++ normaux. Mais grâce aux solutions que les autres gars suggèrent sur la façon dont vous pouvez faire le tableau unidimensionnel c ++ normal à un tableau bidimensionnel, nous pouvons adapter les mêmes idées au tableau std, par exemple selon l'idée de Mehrdad Afshari, nous pouvons écrire le code suivant:
Cette ligne de code crée un "tableau jugged" , qui est un tableau unidimensionnel que chacune de ses cellules est ou pointe vers un autre tableau unidimensionnel.
Si tous les tableaux unidimensionnels dans un tableau unidimensionnel sont égaux dans leur longueur / taille, alors vous pouvez traiter la variable array2d comme un véritable tableau bidimensionnel, et vous pouvez utiliser les méthodes spéciales pour traiter les lignes ou les colonnes, selon la façon dont vous le voyez à l'esprit, dans le tableau 2D, que le tableau std prend en charge.
Vous pouvez également utiliser la solution de Kevin Loney:
mais si vous utilisez un tableau std, le code doit être différent:
Et ont toujours les fonctions uniques du tableau std.
Notez que vous pouvez toujours accéder aux éléments du tableau std à l'aide des
[]
parenthèses, et vous n'avez pas besoin d'appeler laat
fonction. Vous pouvez également définir et affecter une nouvelle variable int qui calculera et conservera le nombre total d'éléments dans le tableau std, et utilisera sa valeur, au lieu de répétersizeX*sizeY
Vous pouvez définir votre propre classe générique de tableau bidimensionnel et définir le constructeur de la classe de tableau bidimensionnel pour recevoir deux entiers afin de spécifier le nombre de lignes et de colonnes dans le nouveau tableau bidimensionnel, et définir la fonction get qui reçoit deux paramètres d'entier qui accèdent à un élément dans le tableau bidimensionnel et renvoie sa valeur, et définissent la fonction qui reçoit trois paramètres, que les deux premiers sont des entiers qui spécifient la ligne et la colonne dans le tableau bidimensionnel, et le troisième paramètre est la nouvelle valeur du élément. Son type dépend du type que vous avez choisi dans la classe générique.
Vous pourrez implémenter tout cela en utilisant soit le tableau c ++ normal (pointeurs ou sans) ou le tableau std et utiliser l'une des idées suggérées par d'autres personnes, et le rendre facile à utiliser comme le tableau cli, ou comme les deux tableau dimensionnel que vous pouvez définir, affecter et utiliser en C #.
la source
Commencez par définir le tableau à l'aide de pointeurs (ligne 1):
la source
L'exemple ci-dessous peut aider,
la source
Si vous voulez un tableau 2d d'entiers, dont les éléments sont alloués séquentiellement en mémoire, vous devez le déclarer comme
où au lieu de x, vous pouvez écrire n'importe quelle dimension, mais n doit être le même à deux endroits. Exemple
doit imprimer 6.
la source
Je vous ai laissé une solution qui fonctionne le mieux pour moi, dans certains cas. Surtout si l'on connaît [la taille de?] Une dimension du tableau. Très utile pour un tableau de caractères, par exemple si nous avons besoin d'un tableau de tailles variables de tableaux de char [20].
La clé est les parenthèses dans la déclaration de tableau.
la source
J'ai utilisé ce système pas élégant mais RAPIDE, FACILE et FONCTIONNEL. Je ne vois pas pourquoi ne peut pas fonctionner car le seul moyen pour le système de permettre de créer un tableau de grande taille et d'accéder aux pièces est sans le couper en plusieurs parties:
la source
Je ne sais pas avec certitude si la réponse suivante n'a pas été fournie, mais j'ai décidé d'ajouter des optimisations locales à l'allocation du tableau 2d (par exemple, une matrice carrée se fait via une seule allocation):
int** mat = new int*[n]; mat[0] = new int [n * n];
Cependant, la suppression se déroule comme ceci en raison de la linéarité de l'allocation ci-dessus:
delete [] mat[0]; delete [] mat;
la source
déclaration dynamique d'un tableau 2D:
Maintenant, dans le code ci-dessus, nous avons pris un double pointeur et lui avons attribué une mémoire dynamique et donné une valeur aux colonnes. Ici, la mémoire allouée est uniquement pour les colonnes, maintenant pour les lignes, nous avons juste besoin d'une boucle for et assignons à chaque ligne une valeur de mémoire dynamique. Maintenant, nous pouvons utiliser le pointeur exactement comme nous utilisons un tableau 2D. Dans l'exemple ci-dessus, nous avons ensuite attribué des nombres aléatoires à notre tableau 2D (pointeur) .C'est tout sur le DMA du tableau 2D.
la source
J'utilise ceci lors de la création d'un tableau dynamique. Si vous avez une classe ou une structure. Et ça marche. Exemple:
la source