web-dev-qa-db-fra.com

C tableau en croissance dynamique

J'ai un programme qui lit une liste "brute" d'entités dans le jeu, et j'ai l'intention de créer un tableau contenant un numéro d'index (int) d'un nombre indéterminé d'entités, pour le traitement de diverses choses. Je voudrais éviter d'utiliser trop de mémoire ou de processeur pour conserver de tels index ...

Une solution simple et rapide que j’utilise jusqu’à présent consiste à déclarer, dans la fonction de traitement principale (focus local), le tableau avec la taille maximale du nombre d’entités de jeu et un autre entier pour en suivre le nombre Ce n'est pas satisfaisant, car chaque liste contient plus de 3000 tableaux, ce qui n'est pas beaucoup, mais donne l'impression d'être un gaspillage, car je pourrai utiliser la solution pour 6-7 listes pour différentes fonctions.

Je n'ai trouvé aucune solution spécifique C (pas C++ ou C #) pour y parvenir. Je peux utiliser des pointeurs, mais j'ai un peu peur de les utiliser (à moins que ce soit la seule façon possible).

Les tableaux ne quittent pas la portée de la fonction locale (ils doivent être transmis à une fonction, puis supprimés), au cas où cela changerait les choses.

Si les pointeurs sont la seule solution, comment puis-je les garder pour éviter les fuites?

110
Balkania

Je peux utiliser des pointeurs, mais j'ai un peu peur de les utiliser.

Si vous avez besoin d'un tableau dynamique, vous ne pouvez pas échapper aux pointeurs. Pourquoi as-tu peur cependant? Ils ne mordront pas (pourvu que vous fassiez attention). Il n'y a pas de tableau dynamique intégré en C, vous devrez simplement en écrire un vous-même. En C++, vous pouvez utiliser le std::vector class intégré. C # et presque tous les autres langages de haut niveau ont également une classe similaire qui gère les tableaux dynamiques pour vous.

Si vous envisagez d’écrire le vôtre, voici quelque chose qui vous aidera à démarrer: la plupart des implémentations de tableaux dynamiques fonctionnent avec un tableau de taille (petite) par défaut, puis, chaque fois que vous manquez d’espace lorsque vous ajoutez un nouvel élément, taille du tableau. Comme vous pouvez le voir dans l'exemple ci-dessous, ce n'est pas très difficile du tout: (j'ai omis les contrôles de sécurité par souci de brièveté)

typedef struct {
  int *array;
  size_t used;
  size_t size;
} Array;

void initArray(Array *a, size_t initialSize) {
  a->array = (int *)malloc(initialSize * sizeof(int));
  a->used = 0;
  a->size = initialSize;
}

void insertArray(Array *a, int element) {
  // a->used is the number of used entries, because a->array[a->used++] updates a->used only *after* the array has been accessed.
  // Therefore a->used can go up to a->size 
  if (a->used == a->size) {
    a->size *= 2;
    a->array = (int *)realloc(a->array, a->size * sizeof(int));
  }
  a->array[a->used++] = element;
}

void freeArray(Array *a) {
  free(a->array);
  a->array = NULL;
  a->used = a->size = 0;
}

Son utilisation est aussi simple:

Array a;
int i;

initArray(&a, 5);  // initially 5 elements
for (i = 0; i < 100; i++)
  insertArray(&a, i);  // automatically resizes as necessary
printf("%d\n", a.array[9]);  // print 10th element
printf("%d\n", a.used);  // print number of elements
freeArray(&a);
181
casablanca

Je peux penser à plusieurs options.

  1. Liste liée. Vous pouvez utiliser une liste chaînée pour créer un tableau de plus en plus dynamique. Mais vous ne pourrez pas faire array[100] sans avoir à parcourir 1-99 au préalable. Et ce ne serait peut-être pas pratique pour vous d'utiliser non plus.
  2. Grand tableau. Créez simplement un tableau avec suffisamment d'espace pour tout
  3. Redimensionner un tableau. Recréez le tableau une fois que vous connaissez la taille et/ou créez un nouveau tableau chaque fois que vous manquez d'espace avec une marge et copiez toutes les données dans le nouveau tableau.
  4. Combinaison de tableau de liste chaînée. Utilisez simplement un tableau de taille fixe et une fois que vous aurez manqué d'espace, créez un nouveau tableau et un lien vers celui-ci (il serait sage de garder une trace du tableau et du lien vers le tableau suivant dans une structure).

Il est difficile de dire quelle option serait la meilleure dans votre situation. Créer simplement un grand ensemble est bien sûr l’une des solutions les plus simples et ne devrait pas vous poser beaucoup de problèmes à moins qu’il soit vraiment grand.

10
Wolph

Quand tu dis

faire un tableau contenant un numéro d'index (int) d'un nombre indéterminé d'entités

vous dites essentiellement que vous utilisez des "pointeurs", mais l'un d'entre eux est un pointeur local étendu à un tableau au lieu d'un pointeur étendu à la mémoire. Puisque vous utilisez déjà conceptuellement des "pointeurs" (c.-à-d. Des numéros d'identification qui font référence à un élément d'un tableau), pourquoi ne pas simplement utiliser des pointeurs normaux ).

Au lieu que vos objets stockent des numéros d'identification de ressources, vous pouvez leur demander de stocker un pointeur. Fondamentalement la même chose, mais beaucoup plus efficace puisque nous évitons de transformer "tableau + index" en "pointeur".

Les pointeurs ne sont pas effrayants si vous les considérez comme des index de tableau pour toute la mémoire (ce qu’ils sont réellement)

2
Lie Ryan

Construire sur Matteo Furlans design, quand il a dit "la plupart des implémentations de tableaux dynamiques fonctionnent à partir d’un tableau de petite taille taille du tableau ". La différence dans le " travaux en cours " ci-dessous est que sa taille ne double pas, elle vise uniquement à utiliser ce qui est requis. J'ai aussi omis les vérifications de sécurité pour plus de simplicité ... Construisant également sur brimboriums idée, j'ai essayé d'ajouter une fonction de suppression au code ...

Le fichier storage.h ressemble à ceci ...

#ifndef STORAGE_H
#define STORAGE_H

#ifdef __cplusplus
extern "C" {
#endif

    typedef struct 
    {
        int *array;
        size_t size;
    } Array;

    void Array_Init(Array *array);
    void Array_Add(Array *array, int item);
    void Array_Delete(Array *array, int index);
    void Array_Free(Array *array);

#ifdef __cplusplus
}
#endif

#endif /* STORAGE_H */

Le fichier storage.c ressemble à ceci ...

#include <stdio.h>
#include <stdlib.h>
#include "storage.h"

/* Initialise an empty array */
void Array_Init(Array *array) 
{
    int *int_pointer;

    int_pointer = (int *)malloc(sizeof(int));

    if (int_pointer == NULL)
    {       
        printf("Unable to allocate memory, exiting.\n");
        free(int_pointer);
        exit(0);
    }
    else
    {
        array->array = int_pointer; 
        array->size = 0;
    }
}

/* Dynamically add to end of an array */
void Array_Add(Array *array, int item) 
{
    int *int_pointer;

    array->size += 1;

    int_pointer = (int *)realloc(array->array, array->size * sizeof(int));

    if (int_pointer == NULL)
    {       
        printf("Unable to reallocate memory, exiting.\n");
        free(int_pointer);
        exit(0);
    }
    else
    {
        array->array = int_pointer;
        array->array[array->size-1] = item;
    }
}

/* Delete from a dynamic array */
void Array_Delete(Array *array, int index) 
{
    int i;
    Array temp;
    int *int_pointer;

    Array_Init(&temp);

    for(i=index; i<array->size; i++)
    {
        array->array[i] = array->array[i + 1];
    }

    array->size -= 1;

    for (i = 0; i < array->size; i++)
    {
        Array_Add(&temp, array->array[i]);
    }

    int_pointer = (int *)realloc(temp.array, temp.size * sizeof(int));

    if (int_pointer == NULL)
    {       
        printf("Unable to reallocate memory, exiting.\n");
        free(int_pointer);
        exit(0);
    }
    else
    {
        array->array = int_pointer; 
    } 
}

/* Free an array */
void Array_Free(Array *array) 
{
  free(array->array);
  array->array = NULL;
  array->size = 0;  
}

Le main.c ressemble à ceci ...

#include <stdio.h>
#include <stdlib.h>
#include "storage.h"

int main(int argc, char** argv) 
{
    Array pointers;
    int i;

    Array_Init(&pointers);

    for (i = 0; i < 60; i++)
    {
        Array_Add(&pointers, i);        
    }

    Array_Delete(&pointers, 3);

    Array_Delete(&pointers, 6);

    Array_Delete(&pointers, 30);

    for (i = 0; i < pointers.size; i++)
    {        
        printf("Value: %d Size:%d \n", pointers.array[i], pointers.size);
    }

    Array_Free(&pointers);

    return (EXIT_SUCCESS);
}

Attendez-vous à la critique constructive à suivre ...

1
user1687619

Eh bien, je suppose que si vous devez supprimer un élément, vous en ferez une copie en dédaignant l'élément à exclure.

// inserting some items
void* element_2_remove = getElement2BRemove();

for (int i = 0; i < vector->size; i++){
       if(vector[i]!=element_2_remove) copy2TempVector(vector[i]);
       }

free(vector->items);
free(vector);
fillFromTempVector(vector);
//

Supposons que getElement2BRemove(), copy2TempVector( void* ...) et fillFromTempVector(...) sont des méthodes auxiliaires permettant de gérer le vecteur temp.

Pour créer un tableau d'éléments illimités de tout type:

typedef struct STRUCT_SS_VECTOR {
    size_t size;
    void** items;
} ss_vector;


ss_vector* ss_init_vector(size_t item_size) {
    ss_vector* vector;
    vector = malloc(sizeof(ss_vector));
    vector->size = 0;
    vector->items = calloc(0, item_size);

    return vector;
}

void ss_vector_append(ss_vector* vec, void* item) {
    vec->size++;
    vec->items = realloc(vec->items, vec->size * sizeof(item));
    vec->items[vec->size - 1] = item;
};

void ss_vector_free(ss_vector* vec) {
    for (int i = 0; i < vec->size; i++)
        free(vec->items[i]);

    free(vec->items);
    free(vec);
}

et comment l'utiliser:

// defining some sort of struct, can be anything really
typedef struct Apple_STRUCT {
    int id;
} Apple;

Apple* init_Apple(int id) {
    Apple* a;
    a = malloc(sizeof(Apple));
    a-> id = id;
    return a;
};


int main(int argc, char* argv[]) {
    ss_vector* vector = ss_init_vector(sizeof(Apple));

    // inserting some items
    for (int i = 0; i < 10; i++)
        ss_vector_append(vector, init_Apple(i));


    // dont forget to free it
    ss_vector_free(vector);

    return 0;
}

Ce vecteur/tableau peut contenir n’importe quel type d’élément et sa taille est complètement dynamique.

0
Sebastian Karlsson