web-dev-qa-db-fra.com

Quelle est la disposition de la mémoire du vecteur de tableaux?

quelqu'un peut-il expliquer la disposition de la mémoire de

std::vector<std::array<int, 5>> vec(2)

fournit-il un bloc de mémoire contigu d'un tableau 2D avec 2 rangées de 5 éléments?

À ma connaissance, le vecteur de vecteurs

std::vector<std::vector<int>> vec(2, std::vector<int>(5))

fournir la disposition en mémoire de deux tableaux contigus de longueur 5 éléments s dans différents emplacements en mémoire.

En sera-t-il de même pour le vecteur de tableaux?

46
Const

Les tableaux n'ont pas d'indirection, mais stockent simplement leurs données "directement". C'est un std::array<int, 5> contient littéralement cinq ints d'affilée, à plat. Et, comme les vecteurs, ils ne mettent pas de remplissage entre leurs éléments, ils sont donc "contigus en interne".

Cependant, le std::array l'objet lui-même peut être plus grand que l'ensemble de ses éléments ! Il est permis d'avoir des "trucs" de fin comme le remplissage. Donc, bien que probable, il n'est pas nécessairement vrai que vos données seront toutes contiguës dans le premier cas.

An int
+----+
|    |
+----+

A vector of 2 x int
+----+----+----+-----+        +----+----+
| Housekeeping | ptr |        | 1  |  2 |
+----+----+----+-----+        +----+----+
                   |          ^
                   \-----------

An std::array<int, 5>
+----+----+----+----+----+----------->
| 1  |  2 |  3 |  4 |  5 | possible cruft/padding....
+----+----+----+----+----+----------->

A vector of 2 x std::array<int, 5>
+----+----+----+-----+        +----+----+----+----+----+----------------------------+----+----+----+----+----+----------->
| Housekeeping | ptr |        | 1  |  2 |  3 |  4 |  5 | possible cruft/padding.... | 1  |  2 |  3 |  4 |  5 | possible cruft/padding....
+----+----+----+-----+        +----+----+----+----+----+----------------------------+----+----+----+----+----+----------->
                   |          ^
                   \-----------

Et, même si c'était le cas, en raison des règles d'alias, si vous pouviez utiliser un seul int* naviguer parmi les 10 numéros serait potentiellement une autre affaire!

Dans l'ensemble, un vecteur de dix ints serait plus clair, complètement compacté et peut-être plus sûr à utiliser.

Dans le cas d'un vecteur de vecteurs, un vecteur n'est en réalité qu'un pointeur plus un certain entretien, d'où l'indirection (comme vous le dites).

58

La grande différence entre std::vector et std::array est-ce std::vector contient un pointeur sur la mémoire qu'il enveloppe, tandis que std::array contient le tableau réel en lui-même.

Cela signifie qu'un vecteur de vecteurs est comme un tableau dentelé .

Pour un vecteur de tableaux, le std::array les objets seront placés de manière contiguë mais séparés de l'objet vectoriel. Notez que le std::array l'objet lui-même peut être plus grand que le tableau qu'il contient, et si c'est le cas, les données ne seront pas contiguës.

Le dernier bit signifie également qu'un tableau (style C simple ou std::array) de std::array peut également ne pas conserver les données de manière contiguë. Le std::array les objets du tableau seront contigus, mais pas les données.

Le seul moyen de garantir des données contiguës pour un tableau "multidimensionnel" est l'imbrication de tableaux de style C simples.

18

La norme C++ ne garantit pas que std::array ne contient aucune charge utile à la fin du tableau, donc vous ne pouvez hélas pas supposer que le premier élément d'un tableau suivant est juste après le dernier élément d'un tableau précédent.

Même si tel était le cas, le comportement en tentant d'atteindre n'importe quel élément d'un tableau par arithmétique de pointeur sur un pointeur vers un élément d'un tableau différent n'est pas défini. En effet, l'arithmétique des pointeurs n'est valide que dans les tableaux.

Ce qui précède s'applique également à un std::array<std::array>.

11
Bathsheba
static_assert(sizeof(std::array<int,5>)==5*sizeof(int));

ce qui précède évite d'avoir un remplissage à la fin d'un std::array. Aucun compilateur majeur ne fera échouer ce qui précède à cette date, et je parierais que ce ne sera pas le cas à l'avenir.

Si et seulement si ce qui précède échoue, alors std::vector<std::array<int,5>> v(2) aura un "espace" entre les std::array S.

Cela n'aide pas autant que vous le souhaitez; un pointeur généré comme suit:

int* ptr = &v[0][0];

n'a qu'un domaine de validité jusqu'à ptr+5, et le déréférencement ptr+5 est un comportement non défini.

Cela est dû aux règles d'alias; vous n'êtes pas autorisé à "passer" la fin d'un objet dans un autre, même si vous savez qu'il est là, à moins que vous n'alliez d'abord à certains types (comme char*) où l'arithmétique de pointeur moins restreinte est permis.

Cette règle, à son tour, existe pour permettre aux compilateurs de raisonner sur les données auxquelles on accède par quel pointeur, sans avoir à prouver que l'arithmétique arbitraire du pointeur vous permettra d'atteindre des objets extérieurs.

Alors:

struct bob {
  int x,y,z;
};

bob b {1,2,3};
int* py = &b.y;

peu importe ce que vous faites avec py en tant que int*, vous ne pouvez pas modifier légalement x ou z avec.

*py = 77;
py[-1]=3;
std::cout << b.x;

le complice peut optimiser la ligne std::cout pour imprimer simplement 1, car le py[-1]=3 peut tenter modifier b.x, mais le faire par ce moyen est un comportement indéfini.

Le même type de restrictions vous empêche de passer du premier tableau de votre std::vector Au second (c'est-à-dire au-delà de ptr+4).

La création de ptr+5 Est légale, mais uniquement en tant que pointeur à une fin. La comparaison de ptr+5 == &v[1][0] N'est pas non plus spécifiée dans le résultat, même si leurs valeurs binaires seront absolument identiques dans tous les compilateurs sur tous les principaux systèmes matériels.

Si vous voulez aller plus loin dans le terrier du lapin, il n'est même pas possible d'implémenter std::vector<int> Dans C++ lui-même en raison de ces restrictions sur l'alias de pointeur. La dernière fois que j'ai vérifié (c'était avant c ++ 17 , mais je n'ai pas vu de résolution en C++ 17), le comité standard travaillait à résoudre ce problème, mais je ne connais pas l'état de tels efforts. (Cela pose moins de problème que vous ne le pensez, car rien ne nécessite que std::vector<int> Soit implémenté en C++ conforme aux normes; il doit simplement avoir un comportement défini par les normes. Il peut utiliser des extensions spécifiques au compilateur en interne.)

6