Warum fügen Compiler nicht automatisch Aufhebungen ein?

63

In Sprachen wie C wird vom Programmierer erwartet, dass er Aufrufe an free einfügt. Warum macht der Compiler das nicht automatisch? Menschen tun dies in einer angemessenen Zeitspanne (Ignorieren von Fehlern), so dass es nicht unmöglich ist.

EDIT: Zum späteren Nachschlagen hier noch eine Diskussion, die ein interessantes Beispiel hat.

Milton Silva
quelle
125
Und deshalb, meine Kinder, bringen wir Ihnen die Berechnungstheorie bei. ;)
Raphael
7
Dies ist kein Berechenbarkeitsproblem, da der Mensch auch nicht in allen Fällen entscheiden kann. Es ist ein Vollständigkeitsproblem; Freigabeanweisungen enthalten Informationen, die, wenn sie entfernt werden, nicht vollständig durch Analyse wiederhergestellt werden können, es sei denn, diese Analyse enthält Informationen zur Implementierungsumgebung und zum erwarteten Betrieb, die der C-Quellcode nicht enthält.
Nat
41
Nein, es ist ein Berechenbarkeitsproblem. Es ist entscheidbar , ob ein bestimmte Teil des Speichers ausgeplant werden soll. Für ein festes Programm keine Benutzereingaben oder andere externe Störungen.
Andrej Bauer
1
Kommentare sind nicht für längere Diskussionen gedacht. Diese Unterhaltung wurde in den Chat verschoben . Alle Kommentare, die sich nicht speziell mit der Frage befassen und wie sie verbessert werden können, werden sofort gelöscht.
Raphael
2
@BorisTreukhov, bitte bring es in den Chatroom. Nein, ich glaube nicht, dass Andrej sagt, dass eine Fluchtanalyse "unmöglich" ist (obwohl es mir ein bisschen unklar ist, was genau das in diesem Zusammenhang bedeutet). Eine präzise Fluchtanalyse ist nicht zu entscheiden . An alle: Bring es bitte in den Chatroom . Bitte posten Sie hier nur Kommentare, die die Frage verbessern sollen - andere Diskussionen und Kommentare sollten im Chatroom gepostet werden.
DW

Antworten:

80

Denn es ist nicht zu entscheiden, ob das Programm den Speicher wieder nutzen wird. Dies bedeutet, dass kein Algorithmus free()in allen Fällen bestimmen kann, wann er aufgerufen werden soll. Dies bedeutet, dass jeder Compiler, der dies versucht hat, notwendigerweise einige Programme mit Speicherlecks und / oder einige Programme erzeugt, die weiterhin den freigegebenen Speicher verwenden. Selbst wenn Sie sichergestellt haben, dass Ihr Compiler niemals den zweiten Compiler ausgeführt hat und dem Programmierer das Einfügen von Aufrufen zur free()Behebung dieser Fehler gestattet hat free(), ist es noch schwieriger , zu wissen, wann dieser Compiler aufgerufen werden muss, free()wenn ein Compiler verwendet wird, der es nicht versucht hat helfen.

David Richerby
quelle
12
Wir haben eine Frage, die die Fähigkeit des Menschen abdeckt , unentscheidbare Probleme zu lösen . Ich kann Ihnen kein Beispiel für ein Programm nennen, das falsch kompiliert würde, da dies davon abhängt, welchen Algorithmus der Compiler verwendet. Aber jeder Algorithmus erzeugt eine falsche Ausgabe für unendlich viele verschiedene Programme.
David Richerby
1
Kommentare sind nicht für längere Diskussionen gedacht. Diese Unterhaltung wurde in den Chat verschoben .
Gilles 'SO- hör auf böse zu sein'
2
Leute, nehmt es mit, um zu plaudern . Alles, was nicht direkt mit der Antwort selbst zusammenhängt und wie sie verbessert werden kann, wird gelöscht.
Raphael
2
Viele Dinge, die Compiler gerne tun, sind im Allgemeinen unentscheidbar. Wir würden nirgendwo in der Compiler-Welt hinkommen, wenn wir uns immer Rices Theorem anschließen würden.
Tikhon Jelvis
3
Das ist irrelevant. Wenn es für alle Compiler unentscheidbar ist, ist es auch für alle Menschen unentscheidbar. Wir erwarten jedoch, dass der Mensch free()richtig einfügt.
Paul Draper
53

Wie David Richerby zu Recht feststellte, ist das Problem im Allgemeinen nicht zu entscheiden. Die Objektlebensfähigkeit ist eine globale Eigenschaft des Programms und kann im Allgemeinen von den Eingaben in das Programm abhängen.

Sogar präzise dynamische Speicherbereinigung ist ein unbestreitbares Problem! Alle echten Müllsammler verwenden die Erreichbarkeit als konservative Annäherung an die Frage, ob ein zugewiesenes Objekt in Zukunft benötigt wird oder nicht. Es ist eine gute Annäherung, aber es ist trotzdem eine Annäherung.

Das stimmt aber nur generell. Eine der berüchtigtsten Auseinandersetzungen in der Informatikbranche ist "es ist im Allgemeinen unmöglich, deshalb können wir nichts tun". Im Gegenteil, es gibt viele Fälle, in denen es möglich ist, Fortschritte zu erzielen.

Implementierungen, die auf der Referenzzählung basieren, sind sehr nah an "dem Compiler, der Freigabezuweisungen einfügt", sodass es schwierig ist, den Unterschied zu erkennen. Die automatische Referenzzählung von LLVM (verwendet in Objective-C und Swift ) ist ein berühmtes Beispiel.

Region Inference und Garbage Collection während der Kompilierung sind derzeit aktive Forschungsbereiche. In deklarativen Sprachen wie ML und Merkur gestaltet sich dies wesentlich einfacher , in denen Sie ein Objekt nach seiner Erstellung nicht mehr ändern können .

In Bezug auf Menschen gibt es drei Hauptmethoden, mit denen Menschen die Allokationslebensdauer manuell verwalten können:

  1. Durch das Verstehen des Programms und des Problems. Menschen können beispielsweise Objekte mit ähnlichen Lebensdauern in dasselbe Zuweisungsobjekt legen. Compiler und Müllsammler müssen darauf schließen, aber die Menschen haben genauere Informationen.
  2. Durch die selektive Verwendung von nicht lokaler Buchhaltung (z. B. Referenzzählung) oder anderen speziellen Zuordnungstechniken (z. B. Zonen) nur bei Bedarf. Wieder kann ein Mensch wissen, wo ein Compiler darauf schließen muss.
  3. Schlecht. Schließlich kennt jeder real implementierte Programme, die langsame Lecks aufweisen. Wenn dies nicht der Fall ist, müssen Programme und interne APIs manchmal um die Lebensdauer des Speichers herum neu strukturiert werden, was die Wiederverwendbarkeit und Modularität verringert.
Pseudonym
quelle
Kommentare sind nicht für eine längere Diskussion. Wenn Sie deklarative vs funktionale diskutieren möchten, tun Sie dies bitte im Chat .
Gilles 'SO- hör auf böse zu sein'
2
Dies ist bei weitem die beste Antwort auf die Frage (die zu viele Antworten nicht einmal beantworten). Sie hätten einen Hinweis auf die Pionierarbeit von Hans Boehm zur konseervativen GK hinzufügen können : en.wikipedia.org/wiki/Boehm_garbage_collector . Ein weiterer interessanter Punkt ist, dass die Datenlebensdauer (oder der Nutzen im erweiterten Sinne) in Bezug auf eine abstrakte Semantik oder ein Ausführungsmodell definiert werden kann. Aber das Thema ist wirklich breit.
Babou
29

Es ist ein Unvollständigkeitsproblem, kein Unentscheidbarkeitsproblem

Es stimmt zwar, dass die optimale Platzierung von Freigabebescheinigungen nicht zu entscheiden ist, aber das ist hier einfach nicht das Problem. Da dies sowohl für den Menschen als auch für den Compiler unentscheidbar ist, ist es unmöglich, immer wissentlich die optimale Platzierung für die Freigabe auszuwählen, unabhängig davon, ob es sich um einen manuellen oder einen automatischen Prozess handelt. Und da niemand perfekt ist, sollte ein ausreichend fortgeschrittener Compiler in der Lage sein, Menschen zu übertreffen, um annähernd optimale Platzierungen zu erraten. Also, Unentscheidbarkeit ist nicht , warum wir explizite Freigabe Aussagen müssen .

Es gibt Fälle, in denen externes Wissen die Platzierung von Freigabeerklärungen beeinflusst. Das Entfernen dieser Anweisungen entspricht dann dem Entfernen eines Teils der Betriebslogik, und das Auffordern eines Compilers, diese Logik automatisch zu generieren, entspricht dem Auffordern, zu erraten, was Sie denken.

Angenommen, Sie schreiben eine Read-Evaluate-Print-Loop (REPL). : Der Benutzer gibt einen Befehl ein und Ihr Programm führt ihn aus. Der Benutzer kann Speicher zuweisen / freigeben, indem er Befehle in Ihren REPL eingibt. Ihr Quellcode würde angeben, was die REPL für jeden möglichen Benutzerbefehl tun soll, einschließlich der Freigabe, wenn der Benutzer den Befehl dafür eingibt.

Wenn der C-Quellcode jedoch keinen expliziten Befehl für die Freigabe der Zuweisung bereitstellt, muss der Compiler darauf schließen, dass er die Zuweisung durchführen soll, wenn der Benutzer den entsprechenden Befehl in REPL eingibt. Ist dieser Befehl "freigeben", "frei" oder etwas anderes? Der Compiler kann nicht wissen, wie der Befehl lauten soll. Selbst wenn Sie in der Logik programmieren, um nach diesem Befehlswort zu suchen, und die REPL es findet, kann der Compiler nicht wissen, dass es mit Freigabe reagieren soll, es sei denn, Sie weisen es ausdrücklich im Quellcode an.

tl; dr Das Problem istCode CQuelle nicht den Compiler mit externem Wissen zur Verfügung stellen. Unentscheidbarkeit ist nicht das Problem, da es dort ist, ob der Prozess manuell oder automatisiert ist.

Nat
quelle
3
Kommentare sind nicht für längere Diskussionen gedacht. Diese Unterhaltung wurde in den Chat verschoben . Alle weiteren Kommentare, die sich nicht speziell mit den Mängeln dieser Antwort befassen und wie sie behoben werden können, werden sofort gelöscht.
Raphael
23

Derzeit ist keine der angegebenen Antworten vollständig korrekt.

Warum fügen Compiler nicht automatisch Aufhebungen ein?

  1. Einige tun. (Ich erkläre es später.)

  2. Trivialerweise können Sie free()kurz vor dem Beenden des Programms aufrufen . In Ihrer Frage besteht jedoch die implizite Notwendigkeit, free()so schnell wie möglich anzurufen .

  3. Das Problem, wann free()ein C-Programm aufgerufen werden muss, sobald der Speicher nicht erreichbar ist, ist nicht zu lösen, dh für jeden Algorithmus, der die Antwort in endlicher Zeit liefert, gibt es einen Fall, den es nicht abdeckt. Dies und viele andere Unentscheidbarkeiten willkürlicher Programme lassen sich anhand des Halteproblems nachweisen .

  4. Ein unentscheidbares Problem kann nicht immer in endlicher Zeit von jedem Algorithmus gelöst werden, sei es von einem Compiler oder von einem Menschen.

  5. Menschen (versuchen es), in eine Teilmenge von C-Programmen zu schreiben , die durch ihren Algorithmus (selbst) auf Speicherrichtigkeit überprüft werden können.

  6. Einige Sprachen erreichen die Nummer 1, indem sie die Nummer 5 in den Compiler einbauen. Sie erlauben keine Programme mit willkürlicher Verwendung der Speicherzuordnung, sondern eine entscheidbare Teilmenge davon. Foth und Rust sind zwei Beispiele für Sprachen mit einer restriktiveren Speicherzuordnung als C malloc(), die (1) erkennen können, ob ein Programm außerhalb ihrer entscheidbaren Menge geschrieben wurde. (2) Aufhebungszuordnungen automatisch einfügen.

Paul Draper
quelle
1
Ich verstehe, wie Rust das macht. Aber ich habe noch nie von einem Forth gehört, der dies tat. Können Sie näher darauf eingehen?
Milton Silva
2
@MiltonSilva, Forth - zumindest die grundlegendste, originellste Implementierung - hat nur einen Stapel, keinen Haufen. Das Verschieben des Aufrufstapelzeigers durch Zuweisen / Aufheben der Zuweisung ist eine Aufgabe, die der Compiler auf einfache Weise ausführen kann. Forth wurde speziell für sehr einfache Hardware entwickelt, und manchmal ist nicht-dynamischer Speicher alles, was funktioniert. Es ist offensichtlich keine praktikable Lösung für nicht-triviale Programme.
Paul Draper
10

"Menschen tun es, also ist es nicht unmöglich" ist ein bekannter Irrtum. Wir verstehen die Dinge, die wir erschaffen, nicht unbedingt (geschweige denn kontrollieren) - Geld ist ein weit verbreitetes Beispiel. Wir neigen dazu, unsere Erfolgsaussichten in technologischen Fragen (manchmal dramatisch) zu überschätzen, insbesondere wenn menschliche Faktoren fehlen.

Die menschlichen Leistungen in der Computerprogrammierung sind sehr schlecht , und das Studium der Informatik (das in vielen Berufsbildungsprogrammen fehlt) hilft zu verstehen, warum dieses Problem nicht einfach behoben werden kann. Wir könnten eines Tages, vielleicht nicht zu weit entfernt, durch künstliche Intelligenz am Arbeitsplatz ersetzt werden. Selbst dann wird es keinen allgemeinen Algorithmus geben, mit dem die Zuordnung automatisch aufgehoben wird.

André Souza Lemos
quelle
1
Der Trugschluss, die Prämisse menschlicher Fehlbarkeit zu akzeptieren und dennoch anzunehmen, dass von Menschen geschaffene Denkmaschinen immer noch unfehlbar (dh besser als Menschen) sind, ist weniger bekannt, aber faszinierender. Die einzige Annahme, von der Handlung ausgehen kann, ist, dass der menschliche Verstand das Potenzial hat , perfekt zu rechnen.
Wildcard
1. Ich habe nie gesagt, dass Denkmaschinen unfehlbar sein könnten. Besser als der Mensch ist in vielen Fällen das, was er bereits ist. 2. Die Erwartung von Perfektion (auch Potenzial) als Handlungsvoraussetzung ist eine Absurdität.
André Souza Lemos
"Wir könnten eines Tages, vielleicht nicht zu weit entfernt, durch künstliche Intelligenz am Arbeitsplatz ersetzt werden." Dies ist insbesondere Unsinn. Der Mensch ist die Quelle der Absicht im System. Ohne Menschen gibt es keinen Zweck für das System. „Künstliche Intelligenz“ , wie die definiert werden könnte Apparenz intelligenter gegenwärtige Zeit Entscheidung Maschinen, brachte in der Tat durch intelligente Entscheidungen eines Programmierer oder Systemdesigner in der Vergangenheit. Wenn es keine Wartung (das muss von einer Person durchgeführt werden), AI (oder jedes System , das ungeöffnet gelassen wird und automatisch vollständig) fehl.
Wildcard
Absicht kommt beim Menschen wie bei Maschinen immer von außen .
André Souza Lemos
Völlig falsch. (Und außerdem definiert "außerhalb" keine Quelle. ) Entweder geben Sie an, dass die Absicht als solche nicht existiert, oder Sie geben an, dass die Absicht existiert, aber nicht von irgendwoher kommt. Vielleicht glauben Sie, dass Absicht unabhängig vom Zweck existieren kann? In diesem Fall missverstehen Sie das Wort "Absicht". In jedem Fall würde eine persönliche Demonstration Ihre Meinung zu diesem Thema in Kürze ändern. Ich werde nach diesem Kommentar abbrechen, da Worte allein kein Verständnis von "Absicht" bewirken können, weshalb eine weitere Diskussion hier sinnlos ist.
Wildcard
9

Das Fehlen einer automatischen Speicherverwaltung ist ein Merkmal der Sprache.

C soll kein Werkzeug zum einfachen Schreiben von Software sein. Es ist ein Tool, mit dem Sie den Computer dazu bringen, das zu tun, was Sie ihm sagen. Dazu gehört das Zuweisen und Aufheben der Speicherzuweisung zum Zeitpunkt Ihrer Wahl. C ist eine einfache Sprache, die Sie verwenden, wenn Sie den Computer präzise steuern möchten oder wenn Sie Dinge auf eine andere Weise tun möchten, als von den Entwicklern der Sprache / Standardbibliothek erwartet.

Jouni Sirén
quelle
Kommentare sind nicht für längere Diskussionen gedacht. Diese Unterhaltung wurde in den Chat verschoben .
DW
2
Wie ist dies eine Antwort auf die (CS-Teil der) Frage?
Raphael
6
@Raphael Informatik bedeutet nicht, dass wir nach obskuren technischen Antworten suchen sollten. Compiler machen viele Dinge, die im allgemeinen Fall unmöglich sind. Wenn wir eine automatische Speicherverwaltung wünschen, können wir diese auf viele Arten implementieren. C tut dies nicht, weil es nicht tun soll.
Jouni Sirén
9

Das Problem ist meist ein historisches Artefakt, keine Unmöglichkeit der Implementierung.

Die Art und Weise, wie die meisten C-Compiler Code erstellen, ist so, dass der Compiler nur jede Quelldatei gleichzeitig sieht. es sieht nie das ganze Programm auf einmal. Wenn eine Quelldatei eine Funktion aus einer anderen Quelldatei oder einer Bibliothek aufruft, sieht der Compiler nur die Headerdatei mit dem Rückgabetyp der Funktion und nicht den tatsächlichen Code der Funktion. Das heißt, wenn es eine Funktion gibt, die einen Zeiger zurückgibt, kann der Compiler nicht feststellen, ob der Speicher, auf den der Zeiger zeigt, freigegeben werden muss oder nicht. Die zu entscheidenden Informationen werden dem Compiler zu diesem Zeitpunkt nicht angezeigt. Dem menschlichen Programmierer steht es frei, den Quellcode der Funktion oder die Dokumentation nachzuschlagen, um herauszufinden, was mit dem Zeiger zu tun ist.

Wenn Sie sich mit modernen Low-Level-Sprachen wie C ++ 11 oder Rust beschäftigen, werden Sie feststellen, dass diese das Problem meistens gelöst haben, indem Sie den Speichertyp explizit in den Zeigertyp aufnehmen. In C ++ würden Sie a unique_ptr<T>anstelle einer Ebene verwenden T*, um den Speicher zu speichern. Dadurch unique_ptr<T>wird sichergestellt, dass der Speicher freigegeben wird, wenn das Objekt im Gegensatz zur Ebene das Ende des Gültigkeitsbereichs erreicht T*. Der Programmierer kann den Speicher von einem unique_ptr<T>zum anderen übergeben, aber es kann immer nur einen geben unique_ptr<T>, der auf den Speicher zeigt. So ist immer klar, wem das Gedächtnis gehört und wann es freigegeben werden muss.

C ++ erlaubt aus Gründen der Abwärtskompatibilität immer noch die manuelle Speicherverwaltung im alten Stil und damit die Erzeugung von Fehlern oder Möglichkeiten, um den Schutz von a zu umgehen unique_ptr<T>. Rust ist insofern noch strenger, als es durch Compiler-Fehler die Regeln für den Speichereigentum erzwingt.

Was die Unentscheidbarkeit, das Problem des Anhaltens und dergleichen betrifft, ist es nicht möglich, für alle Programme zu entscheiden, wann der Speicher freigegeben werden soll, wenn Sie sich an die C-Semantik halten. Für die meisten aktuellen Programme, nicht für akademische Übungen oder Buggy-Software, wäre es jedoch absolut möglich zu entscheiden, wann und wann nicht. Das ist schließlich der einzige Grund, warum der Mensch herausfinden kann, wann er sich überhaupt befreien soll oder nicht.

Grumbel
quelle
Kommentare sind nicht für längere Diskussionen gedacht. Diese Unterhaltung wurde in den Chat verschoben .
Raphael
6

Andere Antworten haben sich darauf konzentriert, ob es möglich ist, Speicherbereinigungen durchzuführen, einige Details darüber, wie es gemacht wird, und einige der Probleme.

Ein Thema, das noch nicht behandelt wurde, ist die unvermeidliche Verzögerung bei der Müllabfuhr. Wenn ein Programmierer in C free () aufruft, steht dieser Speicher sofort zur Wiederverwendung zur Verfügung. (Zumindest theoretisch!) So kann ein Programmierer seine 100-MB-Struktur freigeben, eine Millisekunde später eine weitere 100-MB-Struktur zuweisen und davon ausgehen, dass der Gesamtspeicherbedarf gleich bleibt.

Dies gilt nicht für die Garbage Collection. Speicherbereinigte Systeme haben einige Verzögerungen bei der Rückgabe von nicht verwendetem Speicher auf den Heap. Dies kann erheblich sein. Wenn Ihre 100-MB-Struktur den Rahmen verlässt und ein Millisekunden später Ihr Programm eine weitere 100-MB-Struktur einrichtet, können Sie davon ausgehen, dass Ihr System für kurze Zeit 200 MB verwendet. Dieser "kurze Zeitraum" kann je nach System Millisekunden oder Sekunden betragen, es tritt jedoch immer noch eine Verzögerung auf.

Wenn Sie auf einem PC mit viel RAM und virtuellem Speicher arbeiten, werden Sie dies wahrscheinlich nie bemerken. Wenn Sie jedoch auf einem System mit begrenzteren Ressourcen (z. B. einem eingebetteten System oder einem Telefon) arbeiten, müssen Sie dies ernst nehmen. Dies ist nicht nur theoretisch - ich persönlich habe gesehen, dass dies Probleme verursacht (wie z. B. beim Absturz des Geräts), wenn auf einem WinCE-System mit .NET Compact Framework gearbeitet und in C # entwickelt wurde.

Graham
quelle
Sie könnten theoretisch vor jeder Zuordnung einen GC durchführen.
AdrianN
4
@adrianN Aber in der Praxis wird das nicht gemacht, weil es mental wäre. Grahams Argument bleibt jedoch bestehen: GCs verursachen immer einen erheblichen Overhead, entweder in Bezug auf die Laufzeit oder in Bezug auf den erforderlichen Speicherüberschuss. Sie können dieses Gleichgewicht in beide Richtungen ändern, aber Sie können den Overhead grundsätzlich nicht beseitigen.
Konrad Rudolph
Die "Verzögerung" beim Freigeben von Speicher ist in einem System mit virtuellem Speicher ein größeres Problem als in einem System mit begrenzten Ressourcen. Im ersteren Fall ist es möglicherweise besser, dass ein Programm 100 MB als 200 MB verwendet, selbst wenn auf dem System 200 MB verfügbar sind. Im letzteren Fall ist es jedoch nicht vorteilhaft , den GC früher als erforderlich auszuführen, es sei denn, Verzögerungen wären in einigen Fällen akzeptabler Teile des Codes als während anderer.
Superkatze
Ich verstehe nicht, wie dies versucht, den (CS-Teil der) Frage zu beantworten.
Raphael
1
@Raphael Ich habe ein bekanntes Problem mit dem Prinzip der Speicherbereinigung erklärt, das in CS als einer der grundlegenden Nachteile unterrichtet wird (oder sein sollte). Ich habe sogar meine persönlichen Erfahrungen aus der Praxis gemacht, um zu zeigen, dass es sich nicht um ein rein theoretisches Problem handelt. Wenn Sie etwas darüber nicht verstanden haben, spreche ich gerne mit Ihnen, um Ihre Kenntnisse zu verbessern.
Graham
4

Die Frage geht davon aus, dass der Programmierer eine Freigabe aus anderen Teilen des Quellcodes ableiten soll. Es ist nicht. "Zu diesem Zeitpunkt im Programm ist die Speicherreferenz FOO nicht mehr nützlich" sind Informationen, die nur dem Programmierer bekannt sind bis sie in (in prozeduralen Sprachen) eine Freigabeanweisung codiert sind.

Es unterscheidet sich theoretisch nicht von anderen Codezeilen. Warum fügen Compiler nicht automatisch "An dieser Stelle im Programm das Register BAR auf Eingabe prüfen" oder "Wenn der Funktionsaufruf ungleich Null zurückgibt, das aktuelle Unterprogramm verlassen" ein ? Aus der Sicht des Compilers ist der Grund "Unvollständigkeit", wie in dieser Antwort gezeigt . Aber jedes Programm leidet an Unvollständigkeit, wenn der Programmierer nicht alles gesagt hat, was er weiß.

Im wirklichen Leben handelt es sich bei den Aufhebungsarbeiten um Grunzarbeiten oder Boilerplates. Unsere Gehirne füllen sie automatisch aus und meckern darüber, und das Gefühl "der Compiler könnte es genauso gut oder besser machen" ist wahr. In der Theorie ist dies jedoch nicht der Fall, obwohl uns glücklicherweise andere Sprachen mehr Wahlmöglichkeiten bei der Theorie bieten.

Travis Wilson
quelle
4
"'An dieser Stelle im Programm ist die Speicherreferenz FOO nicht mehr nützlich' sind Informationen, die nur dem Programmierer bekannt sind" - das ist eindeutig falsch. a) Für viele FOO ist es trivial, dies herauszufinden, z. B. lokale Variablen mit Wertsemantik. b) Sie schlagen vor, dass der Programmierer dies immer weiß, was eindeutig eine zu optimistische Annahme ist; Wäre dies der Fall, hätten wir keine schwerwiegenden Fehler aufgrund einer schlechten Speicherbehandlung. Dies ist sicherheitskritische Software. Was wir leider tun.
Raphael
Ich schlage nur vor, dass die Sprache für Fälle entwickelt wurde, in denen der Programmierer weiß, dass FOO nicht mehr nützlich ist. Ich bin damit einverstanden, dass dies normalerweise nicht der Fall ist, und deshalb müssen statische Analysen und / oder Speicherbereinigungen durchgeführt werden. Welches, Hurra, wir tun. Aber die Frage des OP ist, wann diese Dinge nicht so wertvoll sind wie handcodierte Deallocs?
Travis Wilson
4

Was wird getan ? Es gibt Garbage Collection und Compiler, die die Referenzzählung verwenden (Objective-C, Swift). Diejenigen, die Referenzzählungen durchführen, benötigen Hilfe vom Programmierer, indem sie starke Referenzzyklen vermeiden.

Die eigentliche Antwort auf das "Warum" ist, dass Compiler-Autoren keinen Weg gefunden haben, der gut und schnell genug ist, um ihn in einem Compiler nutzbar zu machen. Da Compiler-Autoren normalerweise ziemlich schlau sind, kann man daraus schließen, dass es sehr, sehr schwierig ist, einen Weg zu finden, der gut genug und schnell genug ist.

Einer der Gründe, warum es sehr, sehr schwierig ist, ist natürlich, dass es nicht zu entscheiden ist. Wenn wir in der Informatik von "Entscheidbarkeit" sprechen, meinen wir "die richtige Entscheidung treffen". Menschliche Programmierer können natürlich leicht entscheiden, wo Speicher freigegeben werden soll, da sie nicht auf korrekte Entscheidungen beschränkt sind. Und sie treffen oft Entscheidungen, die falsch sind.

gnasher729
quelle
Ich sehe hier keinen Beitrag.
Babou
3

In Sprachen wie C wird vom Programmierer erwartet, dass er Aufrufe an free einfügt. Warum macht der Compiler das nicht automatisch?

Denn die Lebensdauer eines Speicherblocks hängt von der Entscheidung des Programmierers ab, nicht von der des Compilers.

Das ist es. Dies ist das Design von C. Compiler kann nicht wissen, was die Absicht war, einen Speicherblock zuzuweisen. Menschen können es tun, weil sie den Zweck jedes Speicherblocks kennen und wenn dieser Zweck erfüllt ist, kann er befreit werden. Das gehört zum Design des Programms, das gerade geschrieben wird.

C ist eine einfache Sprache, daher kommt es häufig vor, dass ein Block Ihres Speichers an einen anderen Prozess oder sogar an einen anderen Prozessor übergeben wird. Im Extremfall kann ein Programmierer absichtlich einen Teil des Speichers zuweisen und ihn nie wieder verwenden, nur um Speicherdruck auf andere Teile des Systems auszuüben. Der Compiler kann nicht erkennen, ob der Block noch benötigt wird.

Agent_L
quelle
-1

In Sprachen wie C wird vom Programmierer erwartet, dass er Aufrufe an free einfügt. Warum macht der Compiler das nicht automatisch?

In C und vielen anderen Sprachen gibt es in der Tat eine Möglichkeit, den Compiler dazu zu bringen, dies in den Fällen zu tun, in denen zum Zeitpunkt der Kompilierung klar ist, wann dies getan werden sollte: Verwendung von Variablen mit automatischer Dauer (dh gewöhnlichen lokalen Variablen) . Der Compiler ist dafür verantwortlich, für genügend Speicherplatz für solche Variablen zu sorgen und diesen Speicherplatz freizugeben, wenn ihre (genau definierte) Lebensdauer endet.

Da Arrays mit variabler Länge seit C99 ein C-Merkmal sind, erfüllen Objekte mit automatischer Dauer im Prinzip im Wesentlichen alle Funktionen in C, die dynamisch zugewiesene Objekte mit berechenbarer Dauer erfüllen. In der Praxis können C- Implementierungen der Verwendung von VLAs natürlich erhebliche praktische Grenzen setzen - dh, ihre Größe kann aufgrund der Zuweisung auf dem Stapel begrenzt sein -, dies ist jedoch eine Überlegung zur Implementierung, keine Überlegung zum Sprachdesign.

Die Objekte, deren beabsichtigte Verwendung eine automatische Dauer ausschließt, sind genau diejenigen, deren Lebensdauer zum Zeitpunkt der Kompilierung nicht bestimmt werden kann.

PellMel
quelle