Initialiser un tableau 2D de taille inconnue

J’ai un tableau 2D de caractères, par exemple, char aList[numSsortingngs][maxLength] . idéalement, pendant l’exécution du programme, je veux pouvoir modifier le contenu d’une liste, c’est-à-dire append, modifier ou supprimer des entrées. Comme une liste est susceptible de changer, je ne souhaite pas avoir à recomstackr mon programme après chaque modification pour modifier une liste. Je souhaite donc écrire une liste dans un fichier texte à la fin du programme, puis la relire dans une liste au début de la prochaine exécution du programme.

Cependant, je ne sais pas au début du programme quelle est la valeur de numSsortingngs. (Je n’utilise pas C99, je ne peux donc pas utiliser un VLA et collecter un nombre de chaînes précédentes à partir d’un fichier externe.) Je pouvais, bien sûr, atsortingbuer à numSsortingngs une valeur artificiellement élevée, mais ça grids!

Existe-t-il un moyen de renseigner une liste sans connaître la valeur de numSsortingngs? Je ne pense pas que ce soit le cas (j’ai examiné des questions connexes) mais existe-t-il un autre moyen de réaliser ce dont j’ai besoin?

Si vous voulez vraiment pouvoir supprimer des éléments du milieu de la grid (vos questions ne sont pas claires à ce sujet), vous aurez besoin d’une sorte de structure à liens multiples. Celles-ci sont souvent utilisées pour implémenter des tableaux épars, vous pouvez donc probablement en trouver un déjà fabriqué.

Je parle de quelque chose comme ça:

 +---+ | A | +-|\+ | \ | \ | \ | \ | +----+----+----+ | | C0 | C1 | C2 | ... | +--|-+----+--|-+ | | | | | | +-V--+ +--V-+ | +----+ | R0 |->|a0,0|-------+>|a0,3|--> ... +----+ +--|-+ +--V-+----+ | R1 |-----+----->|a1,2|--> ... +----+ | +--|-+ ... V | ... V ... 

Où A est le nœud racine de l’object, C est un tableau de pointeurs de colonne, R est un tableau de pointeurs de ligne, et chaque cellule pointe vers lui le prochain voisin le long de sa ligne et de sa colonne. Toutes les cellules non explicitement représentées sont supposées avoir une valeur par défaut (généralement NULL ou 0).

C’est une idée simple, mais une implémentation assez pointue, avec beaucoup de chances de gâcher, utilisez donc une bibliothèque déboguée si vous le pouvez.

Vous pouvez utiliser un tableau alloué dynamicment. Utilisez malloc() pour en créer un, realloc() pour en changer la taille et free() lorsque vous avez terminé. Mais cela a déjà été couvert par une autre réponse .

Une autre alternative consiste à utiliser une liste chaînée . De cette façon, vous n’avez pas besoin de realloc() chaque fois que vous souhaitez étendre votre tableau – realloc() peut être assez coûteux s’il doit copier l’intégralité du tableau dans un nouvel emplacement.

La situation que vous avez décrite correspond précisément à l’ malloc : allouer un bloc de mémoire de longueur variable.

Si votre plan est de peupler pendant la lecture du fichier, vous pouvez faire l’une des deux choses suivantes.

Soit stocker le nombre de chaînes que le premier élément du fichier, alors la suggestion de jgottula fonctionnerait bien.

Ou, devez-vous utiliser un tableau? Vous pouvez les lire directement dans une liste chaînée, puis, une fois la lecture terminée, les déplacer dans un tableau et libérer la liste chaînée.

Les tableaux 2D de style C sont généralement désolés, ils ont l’air simples et utiles sur papier, mais la mise en œuvre de la gestion dynamic de la mémoire – gestion des échecs d’allocation et des nettoyages / redimensionnements – est souvent assez complexe en détail.

Ce que vous pouvez faire est quelque chose comme:

 /* * Start with an array that can hold INITIAL_NUM elements of (char*). */ char **aList = (char**)malloc(INITIAL_NUM, sizeof(*aList)); int curIdx = 0, curListSz = INITIAL_NUM; while (more_stuff_to_append) { /* * Still space in the existing list ? If not - resize */ if (curIdx >= INITIAL_NUM) { curListSz += ALLOC_INCREMENT_FOR_ALIST; if ((aList = realloc(aList, curListSz * sizeof(*aList))) == NULL) error_and_yucky_cleanup("can't resize list, out of memory"); } /* * Allocate a new element. * Note that if it's _known_ in advance that all elements * are the same size, then malloc'ing a big block and slicing * that into pieces is more efficient. */ if ((aList[curIdx] = malloc(new_elem_size, sizeof(char)) == NULL) error_and_yucky_cleanup("out of memory"); /* * put the contents into the new buffer, however that's done. */ populate_new_entry(aList[curIdx]); curIdx++; } 

Le gros problème avec ces approches est généralement que le nettoyage est compliqué. Il faut parcourir le tableau et appeler free () sur chaque élément, ainsi que le dernier élément final pour nettoyer la aList elle-même.

Si vous connaissez toutes les tailles à l’avance, vous pouvez allouer un seul bloc de mémoire aList fois une aList et tous les éléments. Cela fonctionne via quelque chose comme:

 #define LISTSZ(lst) (NUMSTRINGS_MAX * sizeof(*(lst))) #define ELEMSZ(lst) (STRINGSIZE_MAX * sizeof(**(lst))) char **aList = malloc(LISTSZ(aList) + NUMSTRINGS * ELEMSZ(aList)); char *curElem = ((char*)aList) + LISTSZ(aList)); int i; for (i = 0; i < NUMSTRINGS_MAX; i++) { aList[i] = curElem; curElem += ELEMSZ(aList); } 

L'avantage de ceci est que le nettoyage est sortingvial - il suffit d'appeler free((char*)aList); et le tout est parti. Mais vous ne pouvez plus le realloc() car cela aList[] pas de nouvel espace au début du bloc mémoire (où aList[] est stocké).

Ces choses constituent de très bonnes raisons d'utiliser des vecteurs C ++; au moins C ++ effectue le nettoyage (par exemple sur les exceptions de mémoire insuffisante) automatiquement.

Vous pouvez allouer dynamicment le tableau:

 char **aList; int i; aList = malloc(sizeof(char *) * numSsortingngs); for (i = 0; i < numStrings; i++) { aList[i] = malloc(maxLength); } 

Si, par hasard, vous pouvez utiliser C ++ au lieu de C, vous pouvez toujours utiliser un vecteur C ++:

 std::vector > aList;