631 votes

Comment déclarer un tableau 2d en C++ en utilisant new ?

Comment déclarer un tableau 2d en utilisant new ?

Comme, pour un tableau "normal", je le ferais :

int* ary = new int[Size]

mais

int** ary = new int[sizeY][sizeX]

a) ne fonctionne pas/compile et b) n'accomplit pas quoi :

int ary[sizeY][sizeX] 

fait.

74 votes

Cela ne fonctionne que si sizeX est constant : int(*ary)[sizeX] = new int[sizeY][sizeX] ; Ce qui est la bonne façon de créer un int[sizeY][sizeX] et où toute la mémoire est contiguë. (Je ne pense pas que cela mérite une réponse, car probablement votre sizeX n'est pas constant

36 votes

Je ne peux pas croire que toutes les douzaines de réponses ci-dessous sont tous erronée et ne répond pas à la question, et pourtant elles sont toutes votées. Le commentaire ci-dessus de Johanes Shaub est la seule réponse correcte à la question suivante . Un tableau 2D et un tableau de pointeurs vers un tableau sont deux choses complètement différentes, que tout le monde confond apparemment.

8 votes

@JohannesSchaub-litb : Ce n'est pas correct à 100%. Il est certain que cela fonctionne dans ce cas, mais il y a une méthode pour que cela fonctionne lorsque toutes les dimensions varient, cf. stackoverflow.com/a/29375830/103167

874voto

Mehrdad Afshari Points 204872

Si la longueur de la ligne est une constante de compilation, C++11 autorise

auto arr2d = new int [nrows][CONSTANT];

Ver cette réponse . Les compilateurs comme gcc qui autorisent les tableaux à longueur variable comme une extension de C++ peuvent utiliser new comme indiqué ici pour obtenir une fonctionnalité de dimension de tableau entièrement variable en cours d'exécution comme le permet C99, mais le C++ ISO portable est limité à la première dimension variable.

Une autre option efficace consiste à effectuer manuellement l'indexation 2d dans un grand tableau 1d, comme suit une autre réponse montre permettant les mêmes optimisations du compilateur qu'un vrai tableau 2D (par exemple, prouver ou vérifier que les tableaux ne s'aliasent pas entre eux ou ne se chevauchent pas).


Sinon, vous pouvez utiliser un tableau de pointeurs vers des tableaux pour permettre la syntaxe 2D comme les tableaux 2D contigus, même si ce n'est pas une allocation unique efficace. Vous pouvez l'initialiser en utilisant une boucle, comme ceci :

int** a = new int*[rowCount];
for(int i = 0; i < rowCount; ++i)
    a[i] = new int[colCount];

Ce qui précède, pour colCount= 5 y rowCount = 4 produirait ce qui suit :

enter image description here

N'oubliez pas de delete chaque ligne séparément avec une boucle, avant d'effacer le tableau de pointeurs. Exemple dans une autre réponse .

170 votes

Rappelez-vous que tout ce qui est alloué avec new est créé sur le tas et doit être désalloué avec la commande delete Pour éviter les fuites, gardez cela à l'esprit et veillez à supprimer cette mémoire du tas lorsque vous avez fini de l'utiliser.

98 votes

Notez également que celui-ci est un tableau de pointeurs, et non de tableaux. Les pointeurs pointent à leur tour vers des tableaux. Il est important de bien préciser les termes, car de nombreux tutoriels se trompent également. Un tableau de tableaux serait contigu, ce qui n'est pas le cas ici.

0 votes

@litb : Est-ce qu'un[][] est considéré comme un "tableau de tableaux" dans la spécification ou comme un tableau multidimensionnel ?

347voto

Kevin Loney Points 3706
int** ary = new int[sizeY][sizeX]

devrait être :

int **ary = new int*[sizeY];
for(int i = 0; i < sizeY; ++i) {
    ary[i] = new int[sizeX];
}

et ensuite le nettoyage serait :

for(int i = 0; i < sizeY; ++i) {
    delete [] ary[i];
}
delete [] ary;

EDITAR: 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 :

int *ary = new int[sizeX*sizeY];

// ary[i][j] is then rewritten as
ary[i*sizeY+j]

65 votes

Il est un peu plus lourd qu'il ne doit l'être, et il alloue plus de blocs que nécessaire. Les tableaux multidimensionnels n'ont besoin que d'un seul bloc de mémoire, pas d'un bloc par ligne. L'allocation d'un seul bloc simplifie également le nettoyage.

14 votes

@Kevin : Allouer un seul bloc contigu est la meilleure solution (moins d'impact sur l'allocateur, meilleure localité, etc). Mais vous n'avez pas à sacrifier l'écriture propre. Voir stackoverflow.com/a/29375830/103167

11 votes

Cela ne devrait-il pas être i*sizeX+j ? Si je me souviens bien, avec l'ordre principal des lignes, cela devrait être row*numColumns+col.

128voto

M. Alaggan Points 750

En C++11, c'est possible :

auto array = new double[M][N]; 

De cette façon, la mémoire n'est pas initialisée. Pour l'initialiser, faites plutôt ceci :

auto array = new double[M][N]();

Exemple de programme (compilé avec "g++ -std=c++11") :

#include <iostream>
#include <utility>
#include <type_traits>
#include <typeinfo>
#include <cxxabi.h>
using namespace std;

int main()
{
    const auto M = 2;
    const auto N = 2;

    // allocate (no initializatoin)
    auto array = new double[M][N];

    // pollute the memory
    array[0][0] = 2;
    array[1][0] = 3;
    array[0][1] = 4;
    array[1][1] = 5;

    // re-allocate, probably will fetch the same memory block (not portable)
    delete[] array;
    array = new double[M][N];

    // show that memory is not initialized
    for(int r = 0; r < M; r++)
    {
        for(int c = 0; c < N; c++)
            cout << array[r][c] << " ";
        cout << endl;
    }
    cout << endl;

    delete[] array;

    // the proper way to zero-initialize the array
    array = new double[M][N]();

    // show the memory is initialized
    for(int r = 0; r < M; r++)
    {
        for(int c = 0; c < N; c++)
            cout << array[r][c] << " ";
        cout << endl;
    }

    int info;
    cout << abi::__cxa_demangle(typeid(array).name(),0,0,&info) << endl;

    return 0;
}

Sortie :

2 4 
3 5 

0 0 
0 0 
double (*) [2]

4 votes

Je dois faire cela dans une classe, donc je ne peux pas utiliser auto. Quel serait le type approprié pour le tableau ?

4 votes

Pouvez-vous utiliser ceci alors : using arr2d = double(*)[2]; arr2d array = new double[M][N];

5 votes

+1 : c'est ce que le PO a demandé. Le type approprié pour cela est soit double (*)[M][N] o double(*)[][N] avec M, N étant des expressions constantes.

64voto

Derecho Points 1122

Je présume, d'après votre exemple de tableau statique, que vous voulez un tableau rectangulaire, et non en dents de scie. Vous pouvez utiliser ce qui suit :

int *ary = new int[sizeX * sizeY];

Vous pouvez alors accéder aux éléments comme :

ary[y*sizeX + x]

N'oubliez pas d'utiliser delete[] sur ary .

2 votes

C'est un bon moyen de le faire. Vous pouvez également utiliser un vecteur<int> de taille X*taille Y pour une sécurité supplémentaire.

7 votes

Le mieux est d'envelopper ce code dans une classe - vous pouvez effectuer un nettoyage dans le destructeur et vous pouvez implémenter les méthodes get(x, y) et set(x,y, val) au lieu de forcer l'utilisateur à faire la multiplication par lui-même. L'implémentation de l'opérateur[] est plus délicate, mais je pense que c'est possible.

37voto

Brian Neal Points 13668

Ne le faites pas.

À moins que vous n'ayez une bonne raison, utilisez std::vector<std::vector<int> > à la place. La gestion de la mémoire sera beaucoup moins sujette aux erreurs.

Editar:

Si vous commencez à rencontrer des problèmes avec la mémoire étant non contiguës comme l'a souligné Dietrich Epp dans les commentaires, vous pouvez passer à l'utilisation de l'option std::vector<int> et allouer des éléments (rangées * colonnes).

Vous pouvez alors mettre la main sur le premier élément de ce vecteur ( &v[0] ) et utiliser les techniques dans les autres réponses pour le traiter comme un produit normal. int[][] .

Cependant, je ne le ferais que si vous aviez réellement profilé votre code et constaté que la mémoire non contiguë causait des problèmes de performance. Ce ne serait probablement un problème que si vous aviez un très grand tableau 2D. C'est du moins ce que je pense. Encore une fois, le profilage est le seul moyen de savoir.

Le point principal de ma réponse était de laisser le vecteur faire la gestion de la mémoire pour vous.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X