Comment gérer correctement les tableaux de pointeurs alloués dynamiquement en C ++?
Actuellement, je travaille sur un microprocesseur qui n'a qu'une petite capacité de RAM (128 Mo). J'exécute plusieurs threads d'exécution pour analyser les performances, mais l'essence du problème réside dans le stockage de grandes quantités de données (flottants et entiers) dans un espace dynamique afin de réduire la possibilité de copie / allocation de données que les structures de données telles que les vecteurs tendent compter sur l'exécution.
J'ai décidé de tester quelques schémas possibles sur la manière de résoudre ce problème dans CLion. J'ai écrit le code suivant:
#include <cstdlib>
#include <cstdio>
int initial_capacity = 3;
int current_capacity = 3;
int current_count = 0;
int*** initializeArray(){
// initialize triple array space in memory
int*** arr;
/*
3 items -> 3 data sets -> each data set starts with 3 spaces
a "int arr[3][3][3]" but dynamically allocated
*/
arr = (int***) calloc(current_capacity, sizeof(int**));
for(int i = 0; i < current_capacity; i++){
arr[i] = (int**) calloc(current_capacity, sizeof(int*));
for(int j =0; j < current_capacity; j++){
arr[i][j] = (int*) calloc(current_capacity,sizeof(int));
}
}
return arr;
}
void resizeArray(int *** arr){
auto max = (double) current_capacity;
double percentage = current_count/max;
if(percentage >= 0.5){
for (int i = 0; i < initial_capacity; ++i) {
for (int j = 0; j < initial_capacity; ++j) {
current_capacity*=2;
arr[i][j] = (int*) realloc(arr[i][j],(current_capacity)*sizeof(int));
}
}
}
}
void deleteArrays(int *** arr){
for (int i = 0; i < initial_capacity; ++i) {
for (int j = 0; j < initial_capacity; ++j) {
for (int k = 0; k < current_count; ++k) {
arr[i][j][k] = NULL;
}
}
}
printf("Releasing allocated arrays 1\n");
for (int i = 0; i < initial_capacity; ++i) {
for (int j = 0; j < initial_capacity; ++j) {
arr[i][j] = (int*) realloc(arr[i][j],sizeof(int));
free(arr[i][j]);
}
}
printf("Releasing allocated arrays 2\n");
for (int i = 0; i < initial_capacity; ++i) {
free(arr[i]);
}
printf("Releasing allocated arrays 3\n");
free(arr);
}
void printArrays(int *** arr){
for (int i = 0; i < 3; ++i) {
printf("Array[%d]\n", i);
for (int j = 0; j < 3; ++j) {
printf("Array[%d][%d]\nElements: ", i, j);
for (int k = 0; k < current_count; ++k) {
printf(" %d", arr[i][j][k]);
}
printf("\n");
}
}
printf("\n");
}
int main() {
int *** generated= initializeArray();
int count = 0;
while (count < 21){
if(count % 3 == 0) printArrays(generated);
//verify
resizeArray(generated);
generated[0][0][current_count] = rand();
generated[0][1][current_count] = rand();
generated[0][2][current_count] = rand();
generated[1][0][current_count] = rand();
generated[1][1][current_count] = rand();
generated[1][2][current_count] = rand();
generated[2][0][current_count] = rand();
generated[2][1][current_count] = rand();
generated[2][2][current_count] = rand();
current_count++;
count++;
}
/* some operation with data collected */
deleteArrays(generated);
printf("Finished deleting dynamic arrays");
return 0;
}
J'ai essayé de générer un triple tableau afin de continuer à stocker la même quantité d'informations dans le dernier sous-ensemble tout en augmentant en taille et en quantité totale. Cependant, la procédure de suppression du tableau afin de compléter l'aspect dynamique du processus entraîne toujours un processus terminé avec le code de sortie -1073740940 (0xC0000374) qui est une erreur de corruption de tas. Je ne suis pas très familier avec cela et tout commentaire à ce sujet serait utile.
La routine resizeArray
double current_capacity
avec current_capacity*=2;
dans chaque itération de la boucle interne. La première fois qu'il est appelé, cela se traduit par arr[0][0]
un pointage vers la mémoire pendant 6 int
, tandis que les boucles continuent et finalement laissent current_capacity
à 1536. Pour le reste de l'exécution du programme, resizeArray
n'alloue jamais plus de mémoire, du fait que son seuil relatif à current_capacity
n'est jamais atteint.
Pendant ce temps, la main
routine continue d'écrire de plus en plus d'éléments generated[0][0]
, augmentant current_count
jusqu'à 20 et donc écrivant au-delà des limites de la mémoire allouée.
Ce bogue peut être corrigé en current_capacity*=2;
sortant des boucles, juste à l'intérieur du bloc «alors» de if(percentage >= 0.5)
.
Notez également que la technique d'implémentation de tableaux multidimensionnels en tant que pointeurs vers pointeurs ou pointeurs vers pointeurs vers pointeurs est inefficace dans le temps et dans l'espace. La «poursuite du pointeur» est mauvaise pour l'exécution spéculative des instructions par le processeur. Pour les tableaux multidimensionnels de taille fixe, cela n'est pas utilisé dans le code de qualité de production. Dans ce cas où une variable de dernière dimension est souhaitée, le choix est un peu moins clair, mais il peut être préférable d'utiliser une allocation de mémoire contiguë et d'utiliser l'arithmétique d'index pour calculer les emplacements dans le tableau plutôt que les recherches de pointeur. (C rend cela assez simple avec des tableaux de longueur variable, bien que leur prise en charge soit facultative. Les implémentations C ++ peuvent fournir des tableaux de longueur variable comme extension, mais l'arithmétique nécessaire n'est pas difficile et peut être effectuée avec des fonctions d'assistance ou des classes.)