2017-09-13 1 views
4

J'examine le comportement de deux tableaux 2D en C++, un attribué à partir de la pile, et un attribué à partir du tas.Pourquoi est-ce que je vois un comportement différent entre les tableaux alloués sur le tas et la pile?

Je crée deux tableaux 2D de même forme et les remplit avec des données. Ensuite, je tente de lire les tableaux dans deux méthodes différentes, la première étant le format d'index de tableau simple "Arr [ROW] [COLUMN]". Ensuite, je lis les tableaux en utilisant un déréférencement de pointeur, et j'obtiens deux résultats différents pour le tableau alloué par tas, mais des résultats identiques pour le tableau alloué par pile. J'essaie de comprendre pourquoi les résultats diffèrent. J'apprécierais toute clarification que quelqu'un peut fournir. Merci d'avance.

Le code que je suis en cours d'exécution est ci-dessous:

#include <iostream> 

using namespace std; 

int main(){ 

    int rows = 6; 
    int columns = 3; 

    // allocate from the stack. 
    double q[rows][columns]; 

    // allocate from the heap. 
    double ** a; 
    a = new double*[rows]; 

    for(int i = 0; i < rows; ++i){ 
     a[i] = new double[columns]; 
    } 

    // populate the arrays. 
    for(int i = 0; i < rows; ++i){ 
     for(int j = 0; j < columns; ++j){ 
      a[i][j] = columns*i+j; 
      q[i][j] = columns*i+j; 
     } 
    } 

    cout << "*****************" << endl; 
    cout << "Array indexing method." << endl; 
    cout << "*****************" << endl; 

    // print the heap allocated array using array indexing. 
    for(int i = 0; i < rows; ++i){ 
     for(int j = 0; j < columns; ++j){ 
      cout << a[i][j] << '\t'; 
     } 
     cout << endl; 
    } 

    cout << "*****************" << endl; 

    // print the stack allocated array using array indexing. 
    for(int i = 0; i < rows; ++i){ 
     for(int j = 0; j < columns; ++j){ 
      cout << q[i][j] << '\t'; 
     } 
     cout << endl; 
    } 

    cout << "*****************" << endl; 
    cout << "Pointer dereferencing method." << endl; 
    cout << "*****************" << endl; 

    // print the heap allocated array. 
    for(int i = 0; i < rows; ++i){ 
     for(int j = 0; j < columns; ++j){ 
      cout << *(&a[0][0] + columns*i + j) << '\t'; 
     } 
     cout << endl; 
    } 

    cout << "*****************" << endl; 

    // print the stack allocated array. 
    for(int i = 0; i < rows; ++i){ 
     for(int j = 0; j < columns; ++j){ 
      cout << *(&q[0][0] + columns*i + j) << '\t'; 
     } 
     cout << endl; 
    } 
    cout << "*****************" << endl; 

    // release the memory allocated to the heap. 
    for(int i = 0; i < rows; ++i){ 
     delete[] a[i]; 
    } 

    delete a; 

    return 0; 
} 

Et les résultats que j'obtiens sont:

***************** 
Array indexing method. 
***************** 
0  1  2 
3  4  5 
6  7  8 
9  10  11 
12  13  14 
15  16  17 
***************** 
0  1  2 
3  4  5 
6  7  8 
9  10  11 
12  13  14 
15  16  17 
***************** 
Pointer dereferencing method. 
***************** 
0  1  2 
0  3  4 
5  0  6 
7  8  0 
9  10  11 
0  12  13 
***************** 
0  1  2 
3  4  5 
6  7  8 
9  10  11 
12  13  14 
15  16  17 
***************** 

Et je peux voir que dans le troisième bloc de sortie, le tableau alloué tas ISN n'est pas lu correctement, mais le tableau alloué à la pile est.

Merci encore.

+3

'deux q [lignes] [colonnes]; 'Les VLA ne sont pas des C++ standard. – user0042

+0

Ce n'est pas le problème, mais avez-vous vraiment besoin de ce que fait 'std :: endl'? ''n''arrête une ligne. –

+0

Non, je ne sais pas, vous avez raison pour cet exemple '\ n' aurait été bien. –

Répondre

11

&q[0][0] vous donne un pointeur sur le premier double dans un bloc contenant rows x columns doubles. Alors que &a[0][0] vous donne un pointeur le premier double dans un bloc contenant columns doubles (vous l'avez alloué en utilisant a[0] = new double[columns];, vous vous souvenez?). Donc, l'accès columns*i + j sera hors limites et déclenchera un comportement indéterminé.

+3

Ah, donc mon problème est que je suppose que l'allocation du tas était contiguë, mais ce n'est pas le cas? Vraisemblablement, chaque rangée de colonnes est assez proche des autres, parce que dans ma sortie, je reçois certaines des valeurs appropriées, mais pas où elles devraient être. –

+2

@JoshM. Vos allocations de tas ne sont pas contiguës du tout. Vous allouez 1 bloc contenant le nombre de pointeurs 'rows' à double, puis le nombre 'rows' de blocs contenant le nombre' columns' de doubles chacun. Bien qu'il soit logique qu'ils soient attribués les uns après les autres (avec un peu de marge entre les deux) dans ce cas particulier, puisque le tas n'est pas encore encombré. Mais il ne faut pas se fier à des suppositions de ce genre sur le comportement de l'allocateur. – VTT

+0

Vous pouvez ajouter ceci à votre réponse: la "méthode de déréférencement du pointeur" invoque UB même sur le tableau alloué par la pile car il indexe les limites du premier sous-réseau. Même si la mémoire est contiguë, cela reste illégal. –

3

Les tableaux sont généralement une allocation unique, dans la pile dans ce cas. La valeur de tableau q[1][0] est positionnée juste après q[0][columns-1]. L'implémentation du pointeur alloue un bloc de mémoire disjoint pour chaque ligne du tas. En faisant référence au-delà de la fin de la première rangée, vous êtes dans undefined comportement territoire.

2

La matrice basée sur la pile est une mémoire entièrement contiguë. Votre segment basé sur un tas est une allocation par ligne, plus une allocation pour contenir toutes ces allocations. La mémoire pour chaque allocation peut être dispersée dans votre tas dans n'importe quel ordre, avec n'importe quelle quantité d'espace entre eux. Vous ne pouvez pas supposer que l'ajout de décalages à la base de l'allocation de la première rangée est valide si vous dépassez sa fin, et c'est certainement un bogue d'essayer d'atteindre une allocation séparée en marchant assez loin de la fin d'une allocation précédente.

Si vous voulez faire ce que vous essayez, changer votre nouvelle expression [] pour être aplatis en un seul tableau:

double * q = new [rows * columns]; 

Ensuite, vous pouvez les indexer comme si elle est un tableau 2d.

Aussi, dans votre message original que vous utilisez le mauvais type de l'appel de suppression sur un, qui est aussi un tableau et doit être supprimé comme l'un (avec [] après.)