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 traiter diverses choses. Je voudrais éviter d'utiliser trop de mémoire ou de CPU pour conserver de tels index ...
Une solution rapide et sale que j'utilise jusqu'à présent consiste à déclarer, dans la fonction de traitement principale (focus local), le tableau avec une taille des entités de jeu maximales, et un autre entier pour garder une trace de combien ont été ajoutés à la liste. Ce n'est pas satisfaisant, car chaque liste contient plus de 3000 tableaux, ce qui n'est pas tant que ça, mais cela ressemble à du gaspillage, car je vais 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 ne soit le seul moyen possible).
Les tableaux ne quittent pas la portée de la fonction locale (ils doivent être passés à une fonction, puis rejetés), au cas où cela changerait les choses.
Si les pointeurs sont la seule solution, comment puis-je les suivre pour éviter les fuites?
la source
Réponses:
Si vous avez besoin d'un tableau dynamique, vous ne pouvez pas échapper aux pointeurs. Pourquoi as-tu peur? Ils ne mordront pas (tant que vous faites attention, c'est-à-dire). Il n'y a pas de tableau dynamique intégré en C, vous devrez juste en écrire un vous-même. En C ++, vous pouvez utiliser la
std::vector
classe intégrée . C # et à peu près tous les autres langages de haut niveau ont également une classe similaire qui gère les tableaux dynamiques pour vous.Si vous prévoyez d'écrire le vôtre, voici quelque chose pour vous aider à démarrer: la plupart des implémentations de tableaux dynamiques fonctionnent en commençant par un tableau d'une (petite) taille par défaut, puis chaque fois que vous manquez d'espace lors de l'ajout d'un nouvel élément, doublez le 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 concision)
Son utilisation est tout aussi simple:
la source
removeArray
méthode qui se débarrasse du dernier élément serait également intéressante. Si vous l'autorisez, je l'ajouterai à votre exemple de code.Il y a quelques options auxquelles je peux penser.
array[100]
sans avoir à vous déplacer au1-99
préalable. Et ce n'est peut-être pas si pratique que vous utilisiez non plus.Il est difficile de dire quelle option serait la meilleure dans votre situation. La simple création d'un grand tableau est bien sûr l'une des solutions les plus simples et ne devrait pas vous poser beaucoup de problèmes à moins qu'elle ne soit vraiment grande.
la source
realloc
avec # 3 - attribuez au tableau une taille normale, puis agrandissez-le chaque fois que vous en manquez.realloc
se chargera de la copie de vos données si nécessaire. Quant à la question du PO sur la gestion de la mémoire, il vous suffit de le fairemalloc
une fois au début,free
une fois à la fin et àrealloc
chaque fois que vous manquez de place. Ce n'est pas si mal.7 * 3264 * 32 bit
sonne comme91.39 kilobytes
. Pas tant que ça de nos jours;)realloc
retour n'est pas tout à fait évidentNULL
:a->array = (int *)realloc(a->array, a->size * sizeof(int));
... Peut-être qu'il aurait été préférable de l'écrire commeint *temp = realloc(a->array, a->size * sizeof *a->array); a->array = temp;
suit : ... De cette façon, il serait évident que tout ce qui se passe doit se produire avant laNULL
valeur est affectée àa->array
(le cas échéant).Comme pour tout ce qui semble plus effrayant au début qu'il ne l'était plus tard, le meilleur moyen de surmonter la peur initiale est de se plonger dans l'inconfort de l'inconnu ! C'est parfois comme cela que nous apprenons le plus, après tout.
Malheureusement, il existe des limites. Pendant que vous apprenez encore à utiliser une fonction, vous ne devriez pas assumer le rôle d'un enseignant, par exemple. Je lis souvent les réponses de ceux qui ne savent apparemment pas comment utiliser
realloc
(c'est-à - dire la réponse actuellement acceptée! ) En disant aux autres comment l'utiliser de manière incorrecte, parfois sous prétexte qu'ils ont omis la gestion des erreurs , même si c'est un écueil courant qui doit être mentionné. Voici une réponse expliquant comment l'utiliserrealloc
correctement . Notez que la réponse stocke la valeur de retour dans une variable différente afin d'effectuer une vérification des erreurs.Chaque fois que vous appelez une fonction et chaque fois que vous utilisez un tableau, vous utilisez un pointeur. Les conversions se produisent implicitement, ce qui devrait être encore plus effrayant, car ce sont les choses que nous ne voyons pas qui causent souvent le plus de problèmes. Par exemple, des fuites de mémoire ...
Les opérateurs de tableau sont des opérateurs de pointeur.
array[x]
est en fait un raccourci pour*(array + x)
, qui peut être décomposé en:*
et(array + x)
. Il est fort probable que ce*
soit ce qui vous trouble. Nous pouvons en outre éliminer l'addition du problème en supposantx
être0
, ainsi,array[0]
devient*array
parce que l'ajout0
ne changera pas la valeur ...... et ainsi nous pouvons voir que cela
*array
équivaut àarray[0]
. Vous pouvez en utiliser un là où vous souhaitez utiliser l'autre, et vice versa. Les opérateurs de tableau sont des opérateurs de pointeur.malloc
,realloc
et les amis n'inventent pas le concept de pointeur que vous utilisez depuis le début; ils l' utilisent simplement pour implémenter une autre fonctionnalité, qui est une forme différente de durée de stockage, la plus appropriée lorsque vous souhaitez des changements de taille drastiques et dynamiques .Il est dommage que la réponse actuellement acceptée va également à l'encontre de certains autres conseils très fondés sur StackOverflow , et en même temps, manque une occasion d'introduire une fonctionnalité peu connue qui brille exactement pour ce cas d'utilisation: le tableau flexible. membres! C'est en fait une réponse assez cassée ... :(
Lorsque vous définissez votre
struct
, déclarez votre tableau à la fin de la structure, sans aucune limite supérieure. Par exemple:Cela vous permettra d'unir votre tableau
int
dans la même allocation que la vôtrecount
, et les relier comme ça peut être très pratique !sizeof (struct int_list)
agira comme s'ilvalue
avait une taille de 0, donc il vous indiquera la taille de la structure avec une liste vide . Vous devez encore ajouter à la taille passée àrealloc
pour spécifier la taille de votre liste.Un autre conseil pratique est de se souvenir que cela
realloc(NULL, x)
équivaut àmalloc(x)
, et nous pouvons l'utiliser pour simplifier notre code. Par exemple:La raison pour laquelle j'ai choisi d'utiliser
struct int_list **
comme premier argument peut ne pas sembler immédiatement évidente, mais si vous pensez au deuxième argument, toute modification apportéevalue
de l'intérieurpush_back
ne serait pas visible pour la fonction à partir de laquelle nous appelons, n'est-ce pas? Il en va de même pour le premier argument, et nous devons être en mesure de modifier notrearray
, non seulement ici mais peut-être aussi dans toute autre fonction à laquelle nous le passons ...array
commence par ne pointer du doigt rien; c'est une liste vide. L'initialiser équivaut à l'ajouter. Par exemple:PS N'oubliez pas
free(array);
quand vous en avez terminé!la source
array[x]
est vraiment un raccourci pour*(array + x)
, [...]" En êtes-vous sûr ???? Voir une exposition de leurs différents comportements: eli.thegreenplace.net/2009/10/21/… .array[index]
est en faitptr[index]
déguisé ... « La définition de l'opérateur d'indexation[]
est queE1[E2]
est identique à(*((E1)+(E2)))
» Vous ne pouvez pas réfuter l'stdint main(void) { unsigned char lower[] = "abcdefghijklmnopqrstuvwxyz"; for (size_t x = 0; x < sizeof lower - 1; x++) { putchar(x[lower]); } }
... Vous aurez probablement besoin de#include <stdio.h>
et<stddef.h>
... Voyez-vous comment j'ai écritx[lower]
(avecx
le type entier) plutôt quelower[x]
? Le compilateur C ne se soucie pas, car il a*(lower + x)
la même valeur que*(x + lower)
, etlower[x]
est le premier où-commex[lower]
est le second. Toutes ces expressions sont équivalentes. Essayez-les ... voyez par vous-même, si vous ne pouvez pas me croire ...gcc
ouclang
pour l'ensemble de votre compilation C, car vous constaterez qu'il y a tellement de packages qui ont adopté les fonctionnalités C99 ...S'appuyant sur la conception de Matteo Furlans , quand il a dit que "la plupart des implémentations de tableaux dynamiques fonctionnent en commençant par un tableau d'une (petite) taille par défaut, puis chaque fois que vous manquez d'espace lors de l'ajout d'un nouvel élément, doublez la taille du tableau ". La différence dans le " travail en cours " ci-dessous est qu'il ne double pas de taille, il vise à n'utiliser que ce qui est nécessaire. J'ai également omis les contrôles de sécurité pour plus de simplicité ... En s'appuyant également sur l' idée de brimboriums , j'ai essayé d'ajouter une fonction de suppression au code ...
Le fichier storage.h ressemble à ceci ...
Le fichier storage.c ressemble à ceci ...
Le main.c ressemble à ceci ...
Attendez-vous à la critique constructive à suivre ...
la source
malloc()
avant d'essayer d'utiliser l'allocation. Dans le même ordre d'idées, c'est une erreur d'attribuer directement le résultat derealloc()
au pointeur à la mémoire d'origine réallouée; en cas d'realloc()
échec,NULL
est retourné et le code est laissé avec une fuite de mémoire. Il est beaucoup plus efficace de doubler la mémoire lors du redimensionnement que d'ajouter 1 espace à la fois: moins d'appels àrealloc()
.int
, etc.) à la fois. Le doublement est une solution typique, mais je ne pense pas qu'il existe une solution optimale qui convienne à toutes les circonstances. Voici pourquoi le doublement est une bonne idée (un autre facteur tel que 1,5 conviendrait également): si vous commencez par une allocation raisonnable, vous n'aurez peut-être pas besoin de réallouer du tout. Lorsque plus de mémoire est nécessaire, l'allocation raisonnable est doublée, et ainsi de suite. De cette façon, vous n'avez probablement besoin que d'un ou deux appelsrealloc()
.Quand tu dis
vous dites essentiellement que vous utilisez des "pointeurs", mais un pointeur local à l'échelle du tableau au lieu d'un pointeur à l'échelle de la mémoire. Puisque vous utilisez déjà conceptuellement des "pointeurs" (c'est-à-dire des numéros d'identification qui font référence à un élément dans un tableau), pourquoi n'utilisez-vous pas simplement des pointeurs réguliers (c'est-à-dire des numéros d'identification qui font référence à un élément du plus grand tableau: toute la mémoire ).
Au lieu que vos objets stockent un numéro d'identification de ressource, vous pouvez leur faire stocker un pointeur à la place. 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 un index de tableau pour toute la mémoire (ce qu'ils sont en réalité)
la source
Pour créer un tableau d’éléments illimités de tout type:
et comment l'utiliser:
Ce vecteur / tableau peut contenir n'importe quel type d'élément et sa taille est complètement dynamique.
la source
Eh bien, je suppose que si vous devez supprimer un élément, vous ferez une copie du tableau en méprisant l'élément à exclure.
Supposons que
getElement2BRemove()
,copy2TempVector( void* ...)
etfillFromTempVector(...)
sont des méthodes auxiliaires pour gérer le vecteur temporaire.la source