Um es klar zu machen: Ich weiß das malloc
und bin free
in der C-Bibliothek implementiert, die normalerweise Speicherblöcke vom Betriebssystem zuweist und eine eigene Verwaltung durchführt, um kleinere Speichermengen an die Anwendung zu verteilen und die Anzahl der zugewiesenen Bytes zu verfolgen . Diese Frage lautet nicht: Woher weiß, wie viel frei ist ?
Ich möchte vielmehr wissen, warum free
dies überhaupt so gemacht wurde. Da ich eine einfache Sprache bin, halte ich es für absolut vernünftig, einen C-Programmierer zu bitten, nicht nur zu verfolgen, welcher Speicher zugewiesen wurde, sondern auch, wie viel (tatsächlich stelle ich häufig fest, dass ich am Ende die Anzahl der Bytes verfolge malloced sowieso). Mir fällt auch ein, dass die explizite Angabe der Anzahl der Bytes, die free
möglicherweise einige Leistungsoptimierungen ermöglichen, z. B. ein Allokator mit separaten Pools für unterschiedliche Zuordnungsgrößen, anhand der Eingabeargumente bestimmen kann, von welchem Pool befreit werden soll Insgesamt wäre weniger Platz erforderlich.
Kurz gesagt, warum wurden malloc
und wurden sie so free
erstellt, dass sie intern die Anzahl der zugewiesenen Bytes verfolgen müssen? Ist es nur ein historischer Unfall?
Eine kleine Änderung: Einige Leute haben Punkte wie "Was ist, wenn Sie einen anderen Betrag als den von Ihnen zugewiesenen freigeben" angegeben? Meine imaginäre API könnte einfach eine erfordern, um genau die Anzahl der zugewiesenen Bytes freizugeben. Mehr oder weniger zu befreien könnte einfach UB oder Implementierung definiert sein. Ich möchte die Diskussion über andere Möglichkeiten jedoch nicht entmutigen.
quelle
malloc
die Größe des zurückgegebenen Blocks nicht kennt .malloc
Gibt häufig einen Block zurück, der größer als angefordert ist. Bestenfalls könnte der Programmierer die immalloc()
Aufruf angeforderte Größe übergeben , was dem Implementierer überhaupt nicht helfen würdefree()
.Antworten:
Ein Argument
free(void *)
(eingeführt in Unix V7) hat einen weiteren großen Vorteil gegenüber den früheren zwei Argumenten,mfree(void *, size_t)
die ich hier nicht erwähnt habe: Ein Argumentfree
vereinfacht jede andere API, die mit Heapspeicher arbeitet, dramatisch . Wenn beispielsweisefree
die Größe des Speicherblocks benötigt wird,strdup
müssten irgendwie zwei Werte (Zeiger + Größe) anstelle von einem (Zeiger) zurückgegeben werden, und C macht Rückgaben mit mehreren Werten viel umständlicher als Rückgaben mit einem Wert. Stattdessenchar *strdup(char *)
müssten wir schreibenchar *strdup(char *, size_t *)
oder sonststruct CharPWithSize { char *val; size_t size}; CharPWithSize strdup(char *)
. (Heutzutage sieht diese zweite Option ziemlich verlockend aus, da wir wissen, dass NUL-terminierte Zeichenfolgen der "katastrophalste Designfehler in der Geschichte des Computing" sind., aber das ist im Nachhinein gesprochen. In den 70er Jahren wurde die Fähigkeit von C, Strings einfach zu handhaben,char *
tatsächlich als entscheidender Vorteil gegenüber Wettbewerbern wie Pascal und Algol angesehen .) Außerdemstrdup
leidet nicht nur dieses Problem, sondern betrifft jedes system- oder benutzerdefinierte Problem Funktion, die Heapspeicher zuweist.Die frühen Unix-Designer waren sehr kluge Leute, und es gibt viele Gründe, warum dies
free
besser ist.mfree
Ich denke, die Antwort auf die Frage ist, dass sie dies bemerkt und ihr System entsprechend entworfen haben. Ich bezweifle, dass Sie eine direkte Aufzeichnung darüber finden werden, was in ihren Köpfen vor sich ging, als sie diese Entscheidung getroffen haben. Aber wir können uns vorstellen.Stellen Sie sich vor, Sie schreiben Anwendungen in C, die unter V6 Unix mit zwei Argumenten ausgeführt werden sollen
mfree
. Sie haben es bisher in Ordnung gebracht, aber das Verfolgen dieser Zeigergrößen wird immer schwieriger, da Ihre Programme ehrgeiziger werden und immer mehr Heap-zugewiesene Variablen verwenden müssen. Aber dann haben Sie eine brillante Idee: Anstatt diesesize_t
ständig zu kopieren , können Sie einfach einige Dienstprogrammfunktionen schreiben, die die Größe direkt im zugewiesenen Speicher speichern:Und je mehr Code Sie mit diesen neuen Funktionen schreiben, desto beeindruckender erscheinen sie. Sie erleichtern nicht nur das Schreiben Ihres Codes, sondern beschleunigen auch Ihren Code - zwei Dinge, die nicht oft zusammenpassen! Bevor Sie diese s überall herumgereicht haben, wurde der CPU-Overhead für das Kopieren erhöht, und Sie mussten häufiger Register (insbesondere für die zusätzlichen Funktionsargumente) verschütten und Speicher verschwenden (da häufig verschachtelte Funktionsaufrufe auftreten in mehreren Kopien des in verschiedenen Stapelrahmen gespeicherten). In Ihrem neuen System müssen Sie noch den Speicher für die Speicherung der
size_t
size_t
size_t
, aber nur einmal, und es wird nirgendwo kopiert. Dies mag wie eine kleine Effizienz erscheinen, aber denken Sie daran, dass es sich um High-End-Maschinen mit 256 KB RAM handelt.Das macht dich glücklich! Sie teilen Ihren coolen Trick also mit den bärtigen Männern, die an der nächsten Unix-Version arbeiten, aber es macht sie nicht glücklich, es macht sie traurig. Sie sehen, sie waren gerade dabei, eine Reihe neuer Dienstprogrammfunktionen wie hinzuzufügen
strdup
, und sie erkennen, dass Leute, die Ihren coolen Trick verwenden, ihre neuen Funktionen nicht verwenden können, da ihre neuen Funktionen alle den umständlichen Zeiger + Größe verwenden API. Und das macht Sie auch traurig, denn Sie erkennen, dass Sie die gutestrdup(char *)
Funktion in jedem Programm, das Sie schreiben, selbst neu schreiben müssen, anstatt die Systemversion verwenden zu können.Aber warte! Dies ist 1977, und die Abwärtskompatibilität wird erst in 5 Jahren erfunden! Und außerdem, niemand ernst tatsächlich nutzt dieses obskure „Unix“ Ding mit Off-Farbbezeichnung. Die erste Ausgabe von K & R ist jetzt auf dem Weg zum Verlag, aber das ist kein Problem - auf der ersten Seite heißt es: "C bietet keine Operationen, um direkt mit zusammengesetzten Objekten wie Zeichenketten umzugehen ... es gibt keinen Heap ... ". Zu diesem Zeitpunkt in der Geschichte
string.h
undmalloc
sind Herstellererweiterungen (!). Also, schlägt Bearded Man # 1 vor, wir können sie ändern, wie wir wollen; Warum erklären wir Ihren kniffligen Allokator nicht einfach zum offiziellen Allokator?Ein paar Tage später sieht Bearded Man # 2 die neue API und sagt: Hey, warte, das ist besser als zuvor, aber es wird immer noch ein ganzes Wort pro Zuordnung ausgegeben, um die Größe zu speichern. Er sieht dies als das Nächste zur Gotteslästerung an. Alle anderen sehen ihn an, als wäre er verrückt, denn was können Sie sonst noch tun? In dieser Nacht bleibt er spät dran und erfindet einen neuen Allokator, der die Größe überhaupt nicht speichert, sondern sie im Handumdrehen durch Bitshifts mit schwarzer Magie auf den Zeigerwert ableitet und sie austauscht, während die neue API an Ort und Stelle bleibt. Die neue API bedeutet, dass niemand den Wechsel bemerkt, aber sie bemerken, dass der Compiler am nächsten Morgen 10% weniger RAM verwendet.
Und jetzt sind alle glücklich: Sie erhalten Ihren einfacher zu schreibenden und schnelleren Code, Bearded Man # 1 kann einen schönen einfachen Code schreiben
strdup
, den die Leute tatsächlich verwenden werden, und Bearded Man # 2 - zuversichtlich, dass er sich seinen Unterhalt für ein bisschen verdient hat - - geht zurück zum Herumspielen mit Quines . Es versenden!Zumindest hätte es so passieren können.
quelle
Weil es nicht nötig ist und es sowieso keinen Sinn ergibt.
Wenn Sie etwas zuweisen, möchten Sie dem System mitteilen, wie viele Bytes zugewiesen werden sollen (aus offensichtlichen Gründen).
Wenn Sie Ihr Objekt jedoch bereits zugewiesen haben, wird jetzt die Größe des Speicherbereichs bestimmt, den Sie zurückerhalten. Es ist implizit. Es ist ein zusammenhängender Speicherblock. Sie können einen Teil davon nicht freigeben (vergessen wir
realloc()
, das ist sowieso nicht das, was es tut), Sie können nur die gesamte Sache freigeben . Sie können X-Bytes auch nicht "freigeben" - Sie geben entweder den Speicherblock frei, von dem Sie ihn erhalten haben,malloc()
oder Sie tun dies nicht.Und jetzt, wenn Sie es freigeben möchten, können Sie dem Speichermanagersystem einfach sagen: "Hier ist dieser Zeiger,
free()
der Block , auf den er zeigt." - und der Speichermanager weiß, wie das geht, entweder weil er die Größe implizit kennt oder weil er möglicherweise nicht einmal die Größe benötigt.Beispielsweise
malloc()
pflegen die meisten typischen Implementierungen eine verknüpfte Liste von Zeigern auf freie und zugewiesene Speicherblöcke. Wenn Sie einen Zeiger an übergebenfree()
, wird nur nach diesem Zeiger in der Liste "zugewiesen" gesucht, die Verknüpfung des entsprechenden Knotens aufgehoben und an die Liste "frei" angehängt. Es brauchte nicht einmal die Größe der Region. Diese Informationen werden nur benötigt, wenn möglicherweise versucht wird, den betreffenden Block wiederzuverwenden.quelle
C ist möglicherweise nicht so "abstrakt" wie C ++, aber es soll immer noch eine Abstraktion über Assembly sein. Zu diesem Zweck werden die Details der untersten Ebene aus der Gleichung herausgenommen. Dies verhindert, dass Sie sich größtenteils mit Ausrichtung und Polsterung herumschlagen müssen, was dazu führen würde, dass alle Ihre C-Programme nicht portierbar sind.
Kurz gesagt, dies ist der gesamte Punkt beim Schreiben einer Abstraktion .
quelle
malloc
N Bytes gefragt haben und stattdessen ein Zeiger auf den Anfang einer ganzen Seite zurückgegeben wird (aufgrund von Ausrichtung, Auffüllen oder anderen Einschränkungen) , gibt es für den Benutzer keine Möglichkeit, den Überblick zu behalten - erzwingt dies Es wäre kontraproduktiv.malloc
kann einfach immer einen ausgerichteten Zeiger zurückgeben, ohne jemals die Größe der Zuordnung zu speichern.free
könnte dann auf die entsprechende Ausrichtung aufrunden, um sicherzustellen, dass alles ordnungsgemäß freigegeben wird. Ich sehe nicht, wo das Problem liegt.size
Parameters anfree
wird außerdem eine weitere Fehlerquelle geöffnet.Tatsächlich hat sich im alten Unix-Kernel-Speicher-Allokator
mfree()
einsize
Argument ergeben.malloc()
undmfree()
behielt zwei Arrays (eines für den Kernspeicher, eines für den Swap), die Informationen zu freien Blockadressen und -größen enthielten.Bis Unix V6 gab es keinen Userspace-Allokator (Programme würden nur verwenden
sbrk()
). In Unix V6 enthielt iolib einen Allokator mitalloc(size)
und einenfree()
Aufruf, für den kein Größenargument erforderlich war . Vor jedem Speicherblock standen seine Größe und ein Zeiger auf den nächsten Block. Der Zeiger wurde nur für freie Blöcke verwendet, wenn die freie Liste durchlaufen wurde, und wurde als Blockspeicher für verwendete Blöcke wiederverwendet.In Unix - 32V und in Unix V7 wurde dies durch eine neue ersetzt
malloc()
undfree()
Implementierung, wofree()
kein nehmen hatsize
Argument. Die Implementierung war eine zirkuläre Liste. Vor jedem Block befand sich ein Wort, das einen Zeiger auf den nächsten Block und ein "besetztes" (zugewiesenes) Bit enthielt. Also habemalloc()/free()
ich nicht einmal eine explizite Größe im Auge behalten.quelle
Weil es nicht muss. Die Informationen sind bereits in der internen Verwaltung von malloc / free verfügbar.
Hier sind zwei Überlegungen (die möglicherweise zu dieser Entscheidung beigetragen haben oder nicht):
Warum sollte eine Funktion einen Parameter erhalten, den sie nicht benötigt?
(Dies würde praktisch den gesamten Client-Code, der auf dynamischem Speicher basiert, komplizieren und Ihrer Anwendung völlig unnötige Redundanz hinzufügen.) Das Verfolgen der Zeigerzuordnung ist bereits ein schwieriges Problem. Das Verfolgen der Speicherzuweisungen zusammen mit den zugehörigen Größen würde die Komplexität des Client-Codes unnötig erhöhen.
Was würde die veränderte
free
Funktion in diesen Fällen bewirken?Würde es nicht frei sein (einen Speicherverlust verursachen?)? Den zweiten Parameter ignorieren? Stoppen Sie die Anwendung, indem Sie exit aufrufen? Die Implementierung würde zusätzliche Fehlerpunkte in Ihrer Anwendung hinzufügen, für eine Funktion, die Sie wahrscheinlich nicht benötigen (und wenn Sie sie benötigen, lesen Sie meinen letzten Punkt unten - "Implementierung der Lösung auf Anwendungsebene").
Weil dies der "richtige" Weg ist, dies zu tun. Eine API sollte die Argumente erfordern, die sie zur Ausführung ihrer Operation benötigt, und nicht mehr .
Die richtigen Möglichkeiten, dies umzusetzen, sind:
(auf Systemebene) innerhalb der Implementierung von malloc - nichts hindert den Bibliotheksimplementierer daran, malloc zu schreiben, um intern verschiedene Strategien basierend auf der empfangenen Größe zu verwenden.
(auf Anwendungsebene), indem Sie malloc und free in Ihre eigenen APIs einbinden und diese stattdessen verwenden (überall in Ihrer Anwendung, wo Sie sie möglicherweise benötigen).
quelle
Fünf Gründe fallen mir ein:
Es ist bequem. Es entfernt eine ganze Menge Overhead vom Programmierer und vermeidet eine Klasse von extrem schwer zu verfolgenden Fehlern.
Es eröffnet die Möglichkeit, einen Teil eines Blocks freizugeben. Da Speichermanager normalerweise Tracking-Informationen benötigen, ist nicht klar, was dies bedeuten würde.
Leichtigkeitsrennen im Orbit sind genau das Richtige für Polsterung und Ausrichtung. Aufgrund der Art der Speicherverwaltung unterscheidet sich die tatsächlich zugewiesene Größe möglicherweise von der von Ihnen gewünschten Größe. Dies bedeutet, dass sowohl
free
eine Größe als auch ein Standort erforderlich sein solltenmalloc
geändert werden müssten, um auch die tatsächlich zugewiesene Größe zurückzugeben.Es ist ohnehin nicht klar, dass das Übergeben der Größe tatsächlich einen Vorteil hat. Ein typischer Speichermanager verfügt über 4 bis 16 Byte Header für jeden Speicherblock, einschließlich der Größe. Dieser Chunk-Header kann für zugewiesenen und nicht zugewiesenen Speicher verwendet werden. Wenn benachbarte Chunks frei werden, können sie zusammengeklappt werden. Wenn Sie den Anrufer dazu bringen, den freien Speicher zu speichern, können Sie wahrscheinlich 4 Bytes pro Block freigeben, indem Sie kein separates Größenfeld im zugewiesenen Speicher haben, aber dieses Größenfeld wird wahrscheinlich sowieso nicht gewonnen, da der Anrufer es irgendwo speichern muss. Aber jetzt sind diese Informationen im Speicher verstreut, anstatt sich vorhersehbar im Header-Block zu befinden, was wahrscheinlich sowieso weniger betrieblich effizient ist.
Auch wenn es ist effizienter ist es radikal unwahrscheinlich Ihr Programm verbringt viel Zeit zu befreien Speicher sowieso so würde der Nutzen klein sein.
Im Übrigen lässt sich Ihre Vorstellung von separaten Zuweisern für Elemente unterschiedlicher Größe ohne diese Informationen leicht umsetzen (anhand der Adresse können Sie bestimmen, wo die Zuordnung erfolgt ist). Dies erfolgt routinemäßig in C ++.
Später hinzugefügt
Eine andere, ziemlich lächerliche Antwort hat std :: allocator als Beweis dafür angeführt , dass
free
dies so funktionieren könnte, aber tatsächlich ist es ein gutes Beispiel dafür, warumfree
dies nicht so funktioniert. Es gibt zwei wesentliche Unterschiede zwischen dem, wasmalloc
/free
do und dem, was std :: allocator tut. Erstensmalloc
undfree
mit Blick auf den Benutzer - sie sind für die allgemeinen Programmierer konzipiert, mit denen sie arbeiten können - währendstd::allocator
ausgelegt ist Spezialist Speicherzuordnung zu der Standardbibliothek zur Verfügung zu stellen. Dies ist ein schönes Beispiel dafür, wann der erste meiner Punkte keine Rolle spielt oder nicht. Da es sich um eine Bibliothek handelt, sind die Schwierigkeiten beim Umgang mit der Komplexität der Tracking-Größe dem Benutzer ohnehin verborgen.Zweitens arbeitet std :: allocator immer mit Elementen gleicher Größe. Dies bedeutet, dass es möglich ist, die ursprünglich übergebene Anzahl von Elementen zu verwenden, um zu bestimmen, wie viel frei ist. Warum dies von sich
free
selbst abweicht , ist illustrativ. Instd::allocator
den Elementen zugeordnet sind immer gleich, bekannt, Größe und immer die gleiche Art von Element sein , so dass sie immer die gleiche Art von Ausrichtungsanforderungen haben. Dies bedeutet, dass der Allokator darauf spezialisiert sein könnte, einfach zu Beginn ein Array dieser Elemente zuzuweisen und sie nach Bedarf zu verteilen. Sie können dies nicht mit tun,free
da es keine Möglichkeit gibt, zu garantieren, dass die beste zurückzugebende Größe die angeforderte Größe ist. Stattdessen ist es viel effizienter, manchmal größere Blöcke zurückzugeben, als der Anrufer von * verlangt, und somit auch nichtDer Benutzer oder der Manager muss die genaue Größe verfolgen, die tatsächlich gewährt wurde. Die Weitergabe dieser Art von Implementierungsdetails an den Benutzer ist ein unnötiger Kopfschmerz, der dem Anrufer keinen Nutzen bringt.- * Wenn jemand diesen Punkt immer noch schwer versteht, bedenken Sie Folgendes: Ein typischer Speicherzuweiser fügt dem Anfang eines Speicherblocks eine kleine Menge von Verfolgungsinformationen hinzu und gibt dann einen Zeigerversatz von diesem zurück. Hier gespeicherte Informationen enthalten beispielsweise normalerweise Zeiger auf den nächsten freien Block. Nehmen wir an, der Header ist nur 4 Byte lang (was tatsächlich kleiner als die meisten realen Bibliotheken ist) und enthält nicht die Größe. Stellen Sie sich dann vor, wir haben einen 20-Byte-freien Block, wenn der Benutzer nach einem 16-Byte-Block fragt, einem naiven Das System würde den 16-Byte-Block zurückgeben, dann aber ein 4-Byte-Fragment hinterlassen, das niemals verwendet werden könnte, um jedes Mal Zeit zu verschwenden
malloc
wird gerufen. Wenn der Manager stattdessen einfach den 20-Byte-Block zurückgibt, werden diese unordentlichen Fragmente vor dem Aufbau bewahrt und der verfügbare Speicher kann sauberer zugeordnet werden. Wenn das System dies jedoch korrekt tun soll, ohne die Größe selbst zu verfolgen, muss der Benutzer für jede einzelne Zuordnung die tatsächlich zugewiesene Speichermenge verfolgen, wenn er diese kostenlos zurückgeben soll. Das gleiche Argument gilt für das Auffüllen von Typen / Zuordnungen, die nicht den gewünschten Grenzen entsprechen. Das Erfordernisfree
einer Größe ist daher höchstens entweder (a) völlig nutzlos, da sich der Speicherzuweiser nicht auf die übergebene Größe verlassen kann, um mit der tatsächlich zugewiesenen Größe übereinzustimmen, oder (b) erfordert sinnlos, dass der Benutzer die tatsächliche Verfolgung der Arbeit ausführt Größe, die von jedem vernünftigen Speichermanager leicht gehandhabt werden kann.quelle
free
Anruf die freizugebende Größe korrekt übergeben soll, muss er dies wissen.Ich poste dies nur als Antwort, nicht weil es die ist, auf die Sie hoffen, sondern weil ich glaube, dass es die einzig plausibel richtige ist:
Es wurde wahrscheinlich ursprünglich als zweckmäßig erachtet und konnte danach nicht verbessert werden.
Es gibt wahrscheinlich keinen überzeugenden Grund dafür. (Aber ich werde dies gerne löschen, wenn gezeigt wird, dass es falsch ist.)
Es würde Vorteile, wenn es möglich war: Sie ein einzelnes großes Stück Speicher zuweisen konnte , dessen Größe Sie vorher kannte, dann zu einem Zeitpunkt ein wenig frei - im Gegensatz zu immer wieder zugeordnet und freigegeben kleine Stücke der Erinnerung. Derzeit sind solche Aufgaben nicht möglich.
Für die vielen (viele 1 !) Von euch, die denken, dass es so lächerlich ist, die Größe zu überschreiten:
Darf ich Sie auf die Entwurfsentscheidung von C ++ für die
std::allocator<T>::deallocate
Methode verweisen ?Ich denke, Sie werden eine ziemlich "interessante" Zeit haben, um diese Entwurfsentscheidung zu analysieren.
Was
operator delete
, es stellt sich heraus , dass der 2013 N3778 Vorschlag ( „C ++ Sized Ausplanung“) das beheben soll, auch.1 Schauen Sie sich einfach die Kommentare unter der ursprünglichen Frage an, um zu sehen, wie viele Personen voreilige Aussagen wie "Die angeforderte Größe ist für den
free
Aufruf völlig nutzlos " gemacht haben , um das Fehlen dessize
Parameters zu rechtfertigen .quelle
malloc()
Implementierung würde es auch die Notwendigkeit beseitigen, sich irgendetwas über eine zugewiesene Region zu merken , wodurch der Zuordnungsaufwand auf den Ausrichtungsaufwand reduziert würde.malloc()
wäre in der Lage, alle Buchhaltung innerhalb der befreiten Stücke selbst zu erledigen. Es gibt Anwendungsfälle, in denen dies eine große Verbesserung wäre. Die Freigabe eines großen Speicherblocks in mehrere kleine Blöcke müsste jedoch nicht empfohlen werden, da dies die Fragmentierung drastisch erhöhen würde.std::allocator
zuzuordnet nur Elemente einer bestimmten, bekannte Größe. Es ist kein Allzweck-Allokator, der Vergleich ist Äpfel mit Orangen.malloc und free gehen Hand in Hand, wobei jedem "malloc" ein "free" entspricht. Daher ist es absolut sinnvoll, dass das "freie" Matching mit einem vorherigen "Malloc" einfach die von diesem Malloc zugewiesene Speichermenge freigibt - dies ist der Hauptanwendungsfall, der in 99% der Fälle sinnvoll wäre. Stellen Sie sich alle Speicherfehler vor, wenn der Programmierer bei allen Verwendungen von malloc / free durch alle Programmierer auf der ganzen Welt den in malloc zugewiesenen Betrag verfolgen und dann daran denken muss, denselben freizugeben. Das Szenario, über das Sie sprechen, sollte wirklich die Verwendung mehrerer Mallocs / Frees in einer Art Speicherverwaltungsimplementierung sein.
quelle
gets
,printf
, manuelle Schleifen (Off-by-one), undefinierten Verhalten, Format-Strings, implizite Konvertierungen, Bit Tricks, et cetera.Ich würde vorschlagen, dass dies daran liegt, dass es sehr praktisch ist, Größeninformationen nicht manuell auf diese Weise zu verfolgen (in einigen Fällen) und auch weniger anfällig für Programmiererfehler.
Außerdem würde realloc diese Buchhaltungsinformationen benötigen, die meiner Meinung nach mehr als nur die Zuordnungsgröße enthalten. dh es ermöglicht die Definition des Mechanismus, nach dem es arbeitet.
Sie könnten Ihren eigenen Allokator schreiben, der in der von Ihnen vorgeschlagenen Weise funktioniert hat, und er wird in C ++ für Pool-Allokatoren häufig auf ähnliche Weise für bestimmte Fälle (mit potenziell massiven Leistungssteigerungen) ausgeführt, obwohl dies im Allgemeinen in Bezug auf den Operator implementiert ist neu für die Zuordnung von Poolblöcken.
quelle
Ich sehe nicht, wie ein Allokator funktionieren würde, der die Größe seiner Allokationen nicht verfolgt. Wenn dies nicht der Fall wäre, wie würde es wissen, welcher Speicher verfügbar ist, um eine zukünftige
malloc
Anforderung zu erfüllen ? Es muss mindestens eine Art Datenstruktur gespeichert werden, die Adressen und Längen enthält, um anzuzeigen, wo sich die verfügbaren Speicherblöcke befinden. (Das Speichern einer Liste freier Speicherplätze entspricht natürlich dem Speichern einer Liste zugewiesener Speicherplätze.)quelle
Nun, das einzige, was Sie brauchen, ist ein Zeiger, mit dem Sie den zuvor zugewiesenen Speicher freigeben. Die Anzahl der Bytes wird vom Betriebssystem verwaltet, sodass Sie sich darüber keine Sorgen machen müssen. Es wäre nicht notwendig, die Anzahl der zugewiesenen Bytes von free () zurückzugeben. Ich schlage Ihnen eine manuelle Methode vor, um die Anzahl der von einem laufenden Programm zugewiesenen Bytes / Positionen zu zählen:
Wenn Sie unter Linux arbeiten und wissen möchten, wie viele Bytes / Positionen malloc zugewiesen hat, können Sie ein einfaches Programm erstellen, das malloc ein- oder n-mal verwendet und die erhaltenen Zeiger druckt. Außerdem müssen Sie das Programm einige Sekunden lang in den Ruhezustand versetzen (genug, um Folgendes zu tun). Führen Sie danach das Programm aus, suchen Sie nach seiner PID, schreiben Sie cd / proc / process_PID und geben Sie einfach "cat maps" ein. Die Ausgabe zeigt Ihnen in einer bestimmten Zeile sowohl die Anfangs- als auch die Endspeicheradresse des Heap-Speicherbereichs (den, in dem Sie den Speicher dinamisch zuweisen). Wenn Sie die Zeiger auf diese zugewiesenen Speicherbereiche ausdrucken, werden Sie kann erraten, wie viel Speicher Sie zugewiesen haben.
Ich hoffe es hilft!
quelle
Warum sollte es? malloc () und free () ist absichtlich sehr einfache Memory - Management - Primitive und geordnete Speicherverwaltung in C ist weitgehend bis zum Entwickler. T.
Darüber hinaus macht realloc () dies bereits - wenn Sie die Zuordnung in realloc () reduzieren, werden die Daten nicht verschoben, und der zurückgegebene Zeiger entspricht dem Original.
Für die gesamte Standardbibliothek gilt im Allgemeinen, dass sie aus einfachen Grundelementen besteht, aus denen Sie komplexere Funktionen erstellen können, die Ihren Anwendungsanforderungen entsprechen. Die Antwort auf jede Frage der Form "Warum macht die Standardbibliothek kein X" ist, dass sie nicht alles kann, was ein Programmierer sich vorstellen kann (dafür sind Programmierer da), und daher nur sehr wenig tut - eigene erstellen oder Verwenden Sie Bibliotheken von Drittanbietern. Wenn Sie eine umfangreichere Standardbibliothek wünschen - einschließlich einer flexibleren Speicherverwaltung -, ist C ++ möglicherweise die Antwort.
Sie haben die Frage C ++ sowie C markiert, und wenn Sie C ++ verwenden, sollten Sie auf keinen Fall malloc / free verwenden - abgesehen von new / delete verwalten STL-Containerklassen den Speicher automatisch und auf eine wahrscheinliche Weise speziell auf die Art der verschiedenen Behälter abgestimmt sein.
quelle
realloc
ist absolut erlaubt, die Daten zu verschieben. Gemäß dem C - Standard ist es völlig legal zu implementierenrealloc
alsmalloc
+memcpy
+free
. Und es gibt gute Gründe, warum eine Implementierung eine verkleinerte Zuordnung verschieben möchte, z. B. um eine Fragmentierung des Speichers zu vermeiden.