Ich habe ein Programm, das eine "rohe" Liste von Entitäten im Spiel liest, und ich beabsichtige, ein Array zu erstellen, das eine Indexnummer (int) einer unbestimmten Anzahl von Entitäten enthält, um verschiedene Dinge zu verarbeiten. Ich möchte vermeiden, zu viel Speicher oder CPU zu verwenden, um solche Indizes zu führen ...
Eine schnelle und schmutzige Lösung, die ich bisher verwende, besteht darin, in der Hauptverarbeitungsfunktion (lokaler Fokus) das Array mit der Größe der maximalen Spielentitäten und eine weitere Ganzzahl zu deklarieren, um zu verfolgen, wie viele zur Liste hinzugefügt wurden. Dies ist nicht zufriedenstellend, da jede Liste mehr als 3000 Arrays enthält, was nicht so viel ist, sich aber wie Verschwendung anfühlt, da ich die Lösung für 6-7 Listen für verschiedene Funktionen verwenden kann.
Ich habe keine C-spezifischen (nicht C ++ oder C #) spezifischen Lösungen gefunden, um dies zu erreichen. Ich kann Zeiger verwenden, habe aber etwas Angst davor, sie zu verwenden (es sei denn, dies ist der einzig mögliche Weg).
Die Arrays verlassen nicht den lokalen Funktionsumfang (sie müssen an eine Funktion übergeben und dann verworfen werden), falls sich dadurch etwas ändert.
Wenn Zeiger die einzige Lösung sind, wie kann ich sie verfolgen, um Undichtigkeiten zu vermeiden?
quelle
Antworten:
Wenn Sie ein dynamisches Array benötigen, können Sie Zeigern nicht entkommen. Warum hast du aber Angst? Sie werden nicht beißen (solange Sie vorsichtig sind). In C gibt es kein eingebautes dynamisches Array. Sie müssen nur eines selbst schreiben. In C ++ können Sie die integrierte
std::vector
Klasse verwenden. C # und fast jede andere Hochsprache haben auch eine ähnliche Klasse, die dynamische Arrays für Sie verwaltet.Wenn Sie vorhaben, Ihre eigenen zu schreiben, sollten Sie zunächst Folgendes tun: Die meisten dynamischen Array-Implementierungen beginnen mit einem Array mit einer (kleinen) Standardgröße. Wenn Sie beim Hinzufügen eines neuen Elements keinen Speicherplatz mehr haben, verdoppeln Sie den Wert Größe des Arrays. Wie Sie im folgenden Beispiel sehen können, ist es überhaupt nicht sehr schwierig: (Ich habe Sicherheitsüberprüfungen der Kürze halber weggelassen)
Die Verwendung ist genauso einfach:
quelle
removeArray
Methode, die das letzte Element entfernt, wäre auch ordentlich. Wenn Sie es zulassen, werde ich es Ihrem Codebeispiel hinzufügen.Ich kann mir einige Optionen vorstellen.
array[100]
ohne zuerst durchzugehen1-99
. Und es ist möglicherweise auch nicht so praktisch für Sie.Es ist schwer zu sagen, welche Option in Ihrer Situation am besten wäre. Das einfache Erstellen eines großen Arrays ist natürlich eine der einfachsten Lösungen und sollte Ihnen nur dann große Probleme bereiten, wenn es wirklich groß ist.
quelle
realloc
mit # 3 zu verwenden - weisen Sie dem Array eine normale Größe zu und vergrößern Sie es dann, wenn Sie keine mehr haben.realloc
übernimmt bei Bedarf das Kopieren Ihrer Daten. Was die Frage des OP zur Speicherverwaltung betrifft, müssen Sie nurmalloc
einmal am Anfang,free
einmal am Ende undrealloc
jedes Mal , wenn Sie keinen Platz mehr haben. Es ist nicht so schlimm.7 * 3264 * 32 bit
klingt wie91.39 kilobytes
. Heutzutage nicht so sehr;)realloc
Rückkehr passieren sollNULL
:a->array = (int *)realloc(a->array, a->size * sizeof(int));
... Vielleicht wäre es am besten geschrieben worden als:int *temp = realloc(a->array, a->size * sizeof *a->array); a->array = temp;
... Auf diese Weise wäre es offensichtlich, dass alles, was passiert, vorher passieren muss DerNULL
Wert wird zugewiesena->array
(falls überhaupt).Wie bei allem, was auf den ersten Blick beängstigender erscheint als später, ist der beste Weg, die anfängliche Angst zu überwinden, in das Unbehagen des Unbekannten einzutauchen ! Es ist manchmal so, wie wir es am meisten lernen.
Leider gibt es Einschränkungen. Während Sie noch lernen, eine Funktion zu verwenden, sollten Sie beispielsweise nicht die Rolle eines Lehrers übernehmen. Ich lese oft Antworten von denen, die anscheinend nicht wissen, wie man sie verwendet
realloc
(dh die derzeit akzeptierte Antwort! ) Und anderen sagt, wie sie falsch verwendet werden sollen, gelegentlich unter dem Deckmantel, dass sie die Fehlerbehandlung weggelassen haben , obwohl dies eine häufige Gefahr ist was erwähnt werden muss. Hier ist eine Antwort, die erklärt, wie manrealloc
richtig verwendet . Beachten Sie, dass die Antwort den Rückgabewert in einer anderen Variablen speichert , um eine Fehlerprüfung durchzuführen.Jedes Mal, wenn Sie eine Funktion aufrufen und wenn Sie ein Array verwenden, verwenden Sie einen Zeiger. Die Konvertierungen erfolgen implizit, was, wenn überhaupt, noch beängstigender sein sollte, da die Dinge, die wir nicht sehen, häufig die meisten Probleme verursachen. Zum Beispiel Speicherlecks ...
Array-Operatoren sind Zeigeroperatoren.
array[x]
ist wirklich eine Abkürzung für*(array + x)
, die unterteilt werden kann in:*
und(array + x)
. Es ist sehr wahrscheinlich, dass Sie*
das verwirrt. Wir können weiterhin die Zugabe von dem Problem beseitigen , indem angenommenx
werden0
somit,array[0]
wird ,*array
da das Hinzufügen0
nicht den Wert ändern ...... und so können wir sehen, dass
*array
das gleichbedeutend ist mitarray[0]
. Sie können eines dort verwenden, wo Sie das andere verwenden möchten, und umgekehrt. Array-Operatoren sind Zeigeroperatoren.malloc
,realloc
Nicht und Freunde erfinden das Konzept eines Zeigers , die Sie die ganze Zeit verwendet haben; Sie verwenden dies lediglich , um eine andere Funktion zu implementieren, bei der es sich um eine andere Form der Speicherdauer handelt, die am besten geeignet ist, wenn Sie drastische, dynamische Größenänderungen wünschen .Es ist eine Schande, dass die derzeit akzeptierte Antwort auch gegen einige andere sehr fundierte Ratschläge zu StackOverflow verstößt und gleichzeitig die Gelegenheit verpasst, eine wenig bekannte Funktion einzuführen, die genau für diesen Anwendungsfall glänzt: flexibles Array Mitglieder! Das ist eigentlich eine ziemlich kaputte Antwort ... :(
Wenn Sie Ihre definieren
struct
, deklarieren Sie Ihr Array am Ende der Struktur ohne Obergrenze. Beispielsweise:Auf diese Weise können Sie Ihr Array
int
in derselben Zuordnung wie Ihre zusammenfassencount
, und es kann sehr praktisch sein , sie so zu binden !sizeof (struct int_list)
verhält sich so, als hättevalue
es eine Größe von 0, sodass die Größe der Struktur mit einer leeren Liste angezeigt wird . Sie müssen noch die übergebene Größe hinzufügen,realloc
um die Größe Ihrer Liste anzugeben.Ein weiterer praktischer Tipp ist, sich daran zu erinnern, dass dies
realloc(NULL, x)
gleichbedeutend istmalloc(x)
, und wir können dies verwenden, um unseren Code zu vereinfachen. Beispielsweise:Der Grund, den ich
struct int_list **
als erstes Argument gewählt habe, mag nicht sofort offensichtlich erscheinen, aber wenn Sie an das zweite Argument denken, sind Änderungen, dievalue
von innen vorgenommen wurden,push_back
für die Funktion, von der wir aufrufen, nicht sichtbar, oder? Das gleiche gilt für das erste Argument, und wir müssen in der Lage sein, unserarray
nicht nur hier, sondern möglicherweise auch in allen anderen Funktionen, an die wir es weitergeben, zu ändern ...array
beginnt auf nichts zu zeigen; Es ist eine leere Liste. Das Initialisieren entspricht dem Hinzufügen. Beispielsweise:PS Denken
free(array);
Sie daran, wenn Sie damit fertig sind!quelle
array[x]
ist wirklich eine Abkürzung für*(array + x)
, [...]" Bist du dir da sicher ???? Sehen Sie sich eine Darstellung ihrer unterschiedlichen Verhaltensweisen an: eli.thegreenplace.net/2009/10/21/… .array[index]
eigentlichptr[index]
in der Verkleidung ... „Die Definition des Index - Operators[]
ist , dassE1[E2]
identisch mit(*((E1)+(E2)))
“ Sie können nicht die std entkräftenint main(void) { unsigned char lower[] = "abcdefghijklmnopqrstuvwxyz"; for (size_t x = 0; x < sizeof lower - 1; x++) { putchar(x[lower]); } }
... Sie müssen wahrscheinlich#include <stdio.h>
und<stddef.h>
... Sehen Sie, wie ich geschrieben habex[lower]
(mitx
dem Integer-Typ) anstattlower[x]
? Dem C-Compiler ist das egal, denn er*(lower + x)
hat den gleichen Wert wie*(x + lower)
undlower[x]
ist der erstere, wo-wiex[lower]
der letztere. Alle diese Ausdrücke sind gleichwertig. Probieren Sie sie aus ... überzeugen Sie sich selbst, wenn Sie mein Wort nicht nehmen können ...gcc
oderclang
für Ihre gesamte C-Kompilierung zu wechseln , da es so viele Pakete gibt, die C99-Funktionen übernommen haben ...Aufbauend auf dem Design von Matteo Furlans , als er sagte, "die meisten dynamischen Array-Implementierungen funktionieren, indem sie mit einem Array mit einer (kleinen) Standardgröße beginnen. Wenn Sie beim Hinzufügen eines neuen Elements keinen Platz mehr haben, verdoppeln Sie die Größe des Arrays. " Der Unterschied bei " work in progress " unten besteht darin, dass es nicht doppelt so groß ist, sondern nur das verwendet, was erforderlich ist. Der Einfachheit halber habe ich auch Sicherheitsüberprüfungen weggelassen ... Aufbauend auf der Idee von brimboriums habe ich versucht, dem Code eine Löschfunktion hinzuzufügen ...
Die Datei storage.h sieht folgendermaßen aus ...
Die Datei storage.c sieht folgendermaßen aus ...
Die main.c sieht so aus ...
Freuen Sie sich auf die konstruktive Kritik ...
quelle
malloc()
bevor versucht wird, die Zuordnung zu verwenden. Ebenso ist es ein Fehler, das Ergebnis direktrealloc()
dem Zeiger auf den ursprünglichen Speicher zuzuweisen, der neu zugewiesen wird. Wenn diesrealloc()
fehlschlägt,NULL
wird zurückgegeben und der Code bleibt mit einem Speicherverlust zurück. Es ist viel effizienter, den Speicher beim Ändern der Größe zu verdoppeln, als jeweils 1 Speicherplatz hinzuzufügen: weniger Aufrufe anrealloc()
.int
usw.) zu erhöhen . Verdoppelung ist eine typische Lösung, aber ich glaube nicht, dass es eine optimale Lösung gibt, die allen Umständen entspricht. Aus diesem Grund ist eine Verdoppelung eine gute Idee (ein anderer Faktor wie 1,5 wäre ebenfalls in Ordnung): Wenn Sie mit einer angemessenen Zuordnung beginnen, müssen Sie möglicherweise überhaupt keine Neuzuweisung vornehmen. Wenn mehr Speicher benötigt wird, wird die angemessene Zuordnung verdoppelt und so weiter. Auf diese Weise benötigen Sie wahrscheinlich nur ein oder zwei Anruferealloc()
.Wenn du sagst
Sie sagen im Grunde, dass Sie "Zeiger" verwenden, aber einen, der ein Array-breiter lokaler Zeiger anstelle eines speicherweiten Zeigers ist. Da Sie konzeptionell bereits "Zeiger" verwenden (dh ID-Nummern, die sich auf ein Element in einem Array beziehen), verwenden Sie einfach reguläre Zeiger (dh ID-Nummern, die sich auf ein Element im größten Array beziehen: den gesamten Speicher) ).
Anstatt dass Ihre Objekte Ressourcen-ID-Nummern speichern, können Sie sie stattdessen dazu bringen, einen Zeiger zu speichern. Grundsätzlich das Gleiche, aber viel effizienter, da wir vermeiden, "Array + Index" in einen "Zeiger" zu verwandeln.
Zeiger sind nicht beängstigend, wenn Sie sie als Array-Index für den gesamten Speicher betrachten (was sie tatsächlich sind).
quelle
So erstellen Sie ein Array mit unbegrenzten Elementen aller Art:
und wie man es benutzt:
Dieser Vektor / Array kann jede Art von Element enthalten und ist vollständig dynamisch.
quelle
Nun, ich denke, wenn Sie ein Element entfernen müssen, werden Sie eine Kopie des Arrays erstellen, das das auszuschließende Element verachtet.
Angenommen
getElement2BRemove()
,copy2TempVector( void* ...)
undfillFromTempVector(...)
sind Hilfsmethoden zur Behandlung des Temperaturvektors.quelle