Warum ist es unmöglich, einen Compiler zu erstellen, der bestimmen kann, ob eine C ++ - Funktion den Wert einer bestimmten Variablen ändert?

104

Ich habe diese Zeile in einem Buch gelesen:

Es ist nachweislich unmöglich, einen Compiler zu erstellen, der tatsächlich bestimmen kann, ob eine C ++ - Funktion den Wert einer bestimmten Variablen ändert oder nicht.

In dem Absatz wurde darüber gesprochen, warum der Compiler bei der Überprüfung der Konstanz konservativ ist.

Warum ist es unmöglich, einen solchen Compiler zu erstellen?

Der Compiler kann jederzeit überprüfen, ob eine Variable neu zugewiesen wird, eine Nicht-Konstanten-Funktion darauf aufgerufen wird oder ob sie als Nicht-Konstanten-Parameter übergeben wird ...

Cricketspieler
quelle
24
Das erste, was mir in den Sinn kommt, sind dynamische Linkbibliotheken. Wenn ich Code auf meinem Computer kompiliere und Sie Code auf Ihrem Computer kompilieren und wir sie zur Laufzeit verknüpfen , wie kann Ihr Compiler dann wissen, ob ich Variablen geändert habe oder nicht?
Mooing Duck
4
@MooingDuck Genau das. Im weiteren Sinne kompiliert der Compiler die Funktion nicht einzeln, sondern als Teil eines umfassenderen Bildes, das möglicherweise nicht alle im Bereich des Compilers liegt.
Called 2voyage
3
"unmöglich" kann eine Übertreibung sein - "rechnerisch nicht realisierbar" (wie bei NP-hard) kann eine bessere Charakterisierung sein, ist für den Schüler jedoch etwas schwieriger zu verstehen. Stellen Sie sich eine verknüpfte Liste oder eine andere abstrakte Datenstruktur vor. Wenn ich eine Funktion aufrufe, die einen Knoten in dieser Liste / Baum / was auch immer ändert, wie könnte ein Compiler jemals hoffen, genau zu beweisen, welcher Knoten geändert wurde (und was vielleicht noch wichtiger ist, welcher nicht), ohne das Programm mit dem vollständig zu simulieren erwartete Eingabe, während es nicht 3 Tage dauert, um eine Quelldatei zu kompilieren ...
Twalberg
36
@twalberg Impossible ist keine Übertreibung, das Halting-Problem gilt hier, wie mehrere Antworten erklären. Es ist einfach nicht möglich, ein allgemeines Programm algorithmisch vollständig zu analysieren.
Fiktik
5
@twalberg Compiler, die nur eine Teilmenge gültiger Programme kompilieren, sind nicht sehr nützlich.
Caleb

Antworten:

139

Warum ist es unmöglich, einen solchen Compiler zu erstellen?

Aus dem gleichen Grund, aus dem Sie kein Programm schreiben können, das bestimmt, ob ein bestimmtes Programm beendet wird. Dies ist als Stoppproblem bekannt und eines der Dinge, die nicht berechenbar sind.

Um klar zu sein, können Sie einen Compiler schreiben, der bestimmen kann, dass eine Funktion die Variable in einigen Fällen ändert , aber Sie können keinen schreiben, der Ihnen zuverlässig sagt, dass die Funktion die Variable (oder den Halt) für ändern wird oder nicht jede mögliche Funktion.

Hier ist ein einfaches Beispiel:

void foo() {
    if (bar() == 0) this->a = 1;
}

Wie kann ein Compiler anhand des Codes feststellen, ob foosich dies jemals ändern wird a? Ob dies der Fall ist oder nicht, hängt von den Bedingungen außerhalb der Funktion ab, nämlich der Implementierung von bar. Der Beweis, dass das Problem des Anhaltens nicht berechenbar ist, ist mehr als das, aber es wird bereits im verlinkten Wikipedia-Artikel (und in jedem Lehrbuch zur Berechnungstheorie) ausführlich erklärt, sodass ich hier nicht versuchen werde, es richtig zu erklären.

Caleb
quelle
47
@mrsoltys, Quantencomputer sind "nur" exponentiell schneller für einige Probleme, sie können unentscheidbare Probleme nicht lösen.
zch
8
@mrsoltys Diese exponentiell komplizierten Algorithmen (wie Factoring) sind perfekt für Quantencomputer, aber das Anhalten des Problems ist ein logisches Dilemma. Es ist nicht berechenbar, egal welche Art von "Computer" Sie haben.
user1032613
7
@mrsoltys, nur um ein Schlauer zu sein, ja, es würde sich ändern. Leider würde dies bedeuten, dass der Algorithmus sowohl beendet ist als auch noch läuft. Leider können Sie nicht sagen, welcher Algorithmus ohne direkte Beobachtung den tatsächlichen Zustand beeinflusst.
Nathan Ernst
9
@ ThorbjørnRavnAndersen: OK, nehme ich an, ich führe ein Programm aus. Wie genau bestimme ich, ob es beendet wird?
Ruakh
8
@ ThorbjørnRavnAndersen Aber wenn Sie das Programm tatsächlich ausführen und es nicht beendet wird (z. B. eine Endlosschleife), werden Sie nie feststellen, dass es nicht beendet wird ... Sie führen einfach einen weiteren Schritt aus, weil es sein könnte der letzte ...
MaxAxeHax
124

Stellen Sie sich vor, ein solcher Compiler existiert. Nehmen wir außerdem an, dass der Einfachheit halber eine Bibliotheksfunktion bereitgestellt wird, die 1 zurückgibt, wenn die übergebene Funktion eine bestimmte Variable ändert, und 0, wenn die Funktion dies nicht tut. Was soll dieses Programm dann drucken?

int variable = 0;

void f() {
    if (modifies_variable(f, variable)) {
        /* do nothing */
    } else {
        /* modify variable */
        variable = 1;
    }
}

int main(int argc, char **argv) {
    if (modifies_variable(f, variable)) {
        printf("Modifies variable\n");
    } else {
        printf("Does not modify variable\n");
    }

    return 0;
}
orlp
quelle
12
Nett! Das Ich bin ein Lügner-Paradoxon, wie es von einem Programmierer geschrieben wurde.
Krumelur
28
Es ist eigentlich nur eine schöne Adaption des berühmten Beweises für die Unentscheidbarkeit des Halteproblems .
Konstantin Weitz
10
In diesem konkreten Fall sollte "modify_variable" true zurückgeben: Es gibt mindestens einen Ausführungspfad, in dem die Variable tatsächlich geändert wird. Und dieser Ausführungspfad wird nach einem Aufruf einer externen, nicht deterministischen Funktion erreicht - die gesamte Funktion ist also nicht deterministisch. Aus diesen beiden Gründen sollte der Compiler die pesimistische Sichtweise einnehmen und entscheiden, dass er die Variable ändert. Wenn der Pfad zum Ändern der Variablen erreicht wird, nachdem ein deterministischer Vergleich (vom Compiler überprüfbar) false ergibt (dh "1 == 1"), kann der Compiler mit Sicherheit sagen, dass eine solche Funktion die Variable niemals ändert
Joe Pineda
6
@JoePineda: Die Frage ist, ob fdie Variable geändert wird - nicht, ob sie die Variable ändern könnte. Diese Antwort ist richtig.
Neil G
4
@JoePineda: Nichts hindert mich daran, den Code modifies_variablevon der Compiler-Quelle zu kopieren / einzufügen , wodurch Ihr Argument vollständig zunichte gemacht wird. (unter der Annahme von Open Source, aber der Punkt sollte klar sein)
Orlp
60

Verwechseln Sie nicht "wird oder wird eine Variable nicht ändern, wenn diese Eingaben gegeben sind" für "hat einen Ausführungspfad, der eine Variable modifiziert".

Ersteres wird als undurchsichtige Prädikatenbestimmung bezeichnet und ist trivial unmöglich zu entscheiden. Abgesehen von der Reduzierung des Stoppproblems können Sie nur darauf hinweisen, dass die Eingaben möglicherweise von einer unbekannten Quelle stammen (z. B. vom Benutzer). Dies gilt für alle Sprachen, nicht nur für C ++.

Die letztere Aussage kann jedoch durch Betrachten des Analysebaums bestimmt werden, was alle optimierenden Compiler tun. Der Grund dafür ist, dass reine Funktionen (und referenziell transparente Funktionen, für eine Definition von referenziell transparent ) alle möglichen netten Optimierungen aufweisen, die angewendet werden können, z. B. leicht inlinierbar zu sein oder ihre Werte zur Kompilierungszeit bestimmen zu lassen. aber wissen , ob eine Funktion rein ist, müssen wir wissen , ob es kann immer eine Variable ändern.

Was also als überraschende Aussage über C ++ erscheint, ist eigentlich eine triviale Aussage über alle Sprachen.

BlueRaja - Danny Pflughoeft
quelle
5
Dies ist die beste Antwort imho, es ist wichtig, diese Unterscheidung zu treffen.
Onkel Zeiv
"trivial unmöglich"?
Kip
2
@Kip "trivial unmöglich zu entscheiden" bedeutet wahrscheinlich "unmöglich zu entscheiden und der Beweis ist trivial".
Fredoverflow
28

Ich denke, das Schlüsselwort in "ob eine C ++ - Funktion den Wert einer bestimmten Variablen ändert oder nicht" ist "wird". Es ist sicherlich möglich, einen Compiler zu erstellen, der prüft, ob eine C ++ - Funktion den Wert einer bestimmten Variablen ändern darf oder nicht. Sie können nicht mit Sicherheit sagen, dass die Änderung stattfinden wird:

void maybe(int& val) {
    cout << "Should I change value? [Y/N] >";
    string reply;
    cin >> reply;
    if (reply == "Y") {
        val = 42;
    }
}
dasblinkenlight
quelle
"Es ist sicherlich möglich, einen Compiler zu erstellen, der prüft, ob eine C ++ - Funktion den Wert einer bestimmten Variablen ändern kann oder nicht." Nein, das ist nicht der Fall. Siehe Calebs Antwort. Damit ein Compiler weiß, ob foo () a ändern kann, muss er wissen, ob bar () 0 zurückgeben kann. Und es gibt keine berechenbare Funktion, die alle möglichen Rückgabewerte einer berechenbaren Funktion erkennen kann. Es gibt also Codepfade, so dass der Compiler nicht erkennen kann, ob sie jemals erreicht werden. Wenn eine Variable nur in einem
Codepfad
12
@MartinEpsz Mit "kann" meine ich "darf sich ändern", nicht "kann sich möglicherweise ändern". Ich glaube, dass OP dies im Sinn hatte, wenn es um const-ness-Checks ging.
Dasblinkenlight
@dasblinkenlight Ich müsste zustimmen, dass ich glaube, dass das OP das erste gemeint hat, "darf sich ändern" oder "darf sich ändern oder nicht" vs. "wird sich definitiv nicht ändern". Natürlich kann ich mir kein Szenario vorstellen, in dem dies ein Problem wäre. Sie können den Compiler sogar so ändern, dass er für jede Funktion, die entweder den Bezeichner oder einen Aufruf einer Funktion mit dem Antwortattribut "darf sich ändern" enthält, einfach mit "kann sich ändern" antwortet. Das heißt, C und C ++ sind schreckliche Sprachen, um dies zu versuchen, da sie eine so lose Definition der Dinge haben. Ich denke, aus diesem Grund wäre Konstanz in C ++ überhaupt ein Problem.
DDS
@MartinEpsz: "Und es gibt keine berechenbare Funktion, die alle möglichen Rückgabewerte einer berechenbaren Funktion erkennen kann." Ich denke, dass das Überprüfen "aller möglichen Rückgabewerte" ein falscher Ansatz ist. Es gibt mathematische Systeme (Maxima, Mathlab), die Gleichungen lösen können, was bedeutet, dass es sinnvoll wäre, einen ähnlichen Ansatz auf Funktionen anzuwenden. Dh behandeln Sie es als eine Gleichung mit mehreren Unbekannten. Die Probleme sind Flusskontrolle + Nebenwirkungen => unlösbare Situationen. IMO, ohne diese (funktionale Sprache, keine Zuordnung / Nebenwirkungen), wäre es möglich gewesen, vorherzusagen, welches
Pfadprogramm
15

Ich denke nicht, dass es notwendig ist, das Stoppproblem aufzurufen, um zu erklären, dass Sie zur Kompilierungszeit nicht algorithmisch wissen können, ob eine bestimmte Funktion eine bestimmte Variable ändert oder nicht.

Stattdessen reicht es aus, darauf hinzuweisen, dass das Verhalten einer Funktion häufig von Laufzeitbedingungen abhängt, die der Compiler nicht im Voraus kennen kann. Z.B

int y;

int main(int argc, char *argv[]) {
   if (argc > 2) y++;
}

Wie kann der Compiler mit Sicherheit vorhersagen, ob yÄnderungen vorgenommen werden?

LarsH
quelle
7

Dies ist möglich und Compiler tun dies für einige Funktionen ständig. Dies ist beispielsweise eine triviale Optimierung für einfache Inline-Accessoren oder viele reine Funktionen.

Was unmöglich ist, ist es im allgemeinen Fall zu wissen.

Immer wenn ein Systemaufruf oder ein Funktionsaufruf von einem anderen Modul oder eine möglicherweise überschriebene Methode aufgerufen wird, kann alles passieren, einschließlich der feindlichen Übernahme durch die Verwendung eines Stapelüberlaufs durch einen Hacker zum Ändern einer nicht verwandten Variablen.

Sie sollten jedoch const verwenden, Globals vermeiden, Verweise auf Zeiger bevorzugen, die Wiederverwendung von Variablen für nicht verwandte Aufgaben usw. vermeiden, um das Leben des Compilers bei aggressiven Optimierungen zu vereinfachen.

kriss
quelle
1
Wenn ich mich richtig erinnere, ist das der springende Punkt der funktionalen Programmierung, oder? Durch die Verwendung nur rein deterministischer Funktionen ohne Nebenwirkungen können Compiler aggressive Optimierungen, Vorausführung, Nachausführung, Memoisierung und sogar Ausführung zur Kompilierungszeit durchführen. Der Punkt , dass ich eine Menge des Beantworters denken ignorieren (oder verwirrt über) ist , dass es ist in der Tat möglich , für eine gut erzogene Teilmenge aller Programme . Und nein, diese Untergruppe ist nicht trivial oder uninteressant, sondern sehr nützlich. Aber es ist in der Tat unmöglich für den absoluten allgemeinen Fall.
Joe Pineda
Überladen ist ein Konzept zur Kompilierungszeit. Sie meinten wahrscheinlich "überschriebene Methode".
Fredoverflow
@FredOverflow: Ja, ich meine Overriden. Überladen ist in der Tat ein Konzept zur Kompilierungszeit. Vielen Dank, dass Sie es entdeckt haben (natürlich kann der Compiler immer noch Probleme haben, es zu analysieren, wenn die Implementierung von einer anderen Kompilierungseinheit stammt, aber das habe ich nicht gemeint). Ich werde die Antwort korrigieren.
kriss
6

Es gibt mehrere Möglichkeiten, dies zu erklären, von denen eine das Halteproblem ist :

In der Berechenbarkeitstheorie kann das Stoppproblem wie folgt angegeben werden: "Entscheiden Sie anhand einer Beschreibung eines beliebigen Computerprogramms, ob das Programm beendet wird oder für immer weiter ausgeführt wird." Dies entspricht dem Problem, bei einem Programm und einer Eingabe zu entscheiden, ob das Programm schließlich angehalten wird, wenn es mit dieser Eingabe ausgeführt wird, oder für immer ausgeführt wird.

Alan Turing bewies 1936, dass ein allgemeiner Algorithmus zur Lösung des Halteproblems für alle möglichen Programm-Eingabe-Paare nicht existieren kann.

Wenn ich ein Programm schreibe, das so aussieht:

do tons of complex stuff
if (condition on result of complex stuff)
{
    change value of x
}
else
{
    do not change value of x
}

Ändert sich der Wert von x? Um dies festzustellen, müssten Sie zuerst feststellen, ob das do tons of complex stuffTeil den Zustand auslöst - oder noch grundlegender, ob es anhält. Das kann der Compiler nicht.

Timothy Shields
quelle
6

Wirklich überrascht, dass es keine Antwort gibt, die das Stoppproblem direkt nutzt! Es gibt eine sehr einfache Reduzierung von diesem Problem auf das Stoppproblem.

Stellen Sie sich vor, der Compiler könnte feststellen, ob eine Funktion den Wert einer Variablen geändert hat oder nicht. Dann wäre es sicherlich möglich zu erkennen, ob die folgende Funktion den Wert von y ändert oder nicht, vorausgesetzt, der Wert von x kann in allen Aufrufen im Rest des Programms verfolgt werden:

foo(int x){
   if(x)
       y=1;
}

Schreiben wir es für jedes Programm, das wir mögen, wie folgt um:

int y;
main(){
    int x;
    ...
    run the program normally
    ...
    foo(x);
}

Beachten Sie, dass unser Programm, wenn und nur wenn es den Wert von y ändert, beendet wird - foo () ist das Letzte, was es vor dem Beenden tut. Das heißt, wir haben das Halteproblem gelöst!

Die obige Reduzierung zeigt uns, dass das Problem, festzustellen, ob sich der Wert einer Variablen ändert, mindestens so schwer ist wie das Stoppproblem. Es ist bekannt, dass das Stoppproblem inkompatibel ist, daher muss dies auch sein.

John Doucette
quelle
Ich bin mir nicht sicher, ob ich Ihrer Argumentation folge, warum unser Programm beendet wird, wenn es den Wert von ändert y. Sieht für mich foo()nach einer schnellen Rückkehr aus und geht dann wieder main(). (Außerdem foo()
rufst
1
@LarsH: Wenn das geänderte Programm beendet wird, war die letzte aufgerufene Funktion f. Wenn y geändert wurde, wurde f aufgerufen (die anderen Anweisungen können y nicht ändern, da es nur durch die Änderung eingeführt wurde). Wenn y geändert wurde, wird das Programm beendet.
MSalters
4

Sobald eine Funktion eine andere Funktion aufruft, deren Quelle der Compiler nicht "sieht", muss er entweder davon ausgehen, dass die Variable geändert wurde, oder es kann weiter unten ein Fehler auftreten. Angenommen, wir haben dies in "foo.cpp":

 void foo(int& x)
 {
    ifstream f("f.dat", ifstream::binary);
    f.read((char *)&x, sizeof(x));
 }

und wir haben dies in "bar.cpp":

void bar(int& x)
{
  foo(x);
}

Wie kann der Compiler "wissen", dass xsich nichts ändert (oder sich angemessener ändert) bar?

Ich bin sicher, wir können uns etwas Komplexeres einfallen lassen, wenn dies nicht komplex genug ist.

Mats Petersson
quelle
Der Compiler kann wissen, dass sich x in bar nicht ändert, wenn bar x als Referenzübergabe an const übergeben wird, oder?
Cricketspieler
Ja, aber wenn ich ein const_castin foo hinzufüge , würde es immer noch xÄnderungen vornehmen - ich würde gegen den Vertrag verstoßen, der besagt, dass Sie keine constVariablen ändern sollen , aber da Sie alles in "mehr const" konvertieren können und const_castexistieren, Die Designer der Sprache hatten sicherlich die Idee, dass es manchmal gute Gründe gibt zu glauben, dass constWerte geändert werden müssen.
Mats Petersson
@MatsPetersson: Ich glaube, wenn Sie const_cast verwenden, können Sie alle Teile behalten, die brechen, weil der Compiler dies kann, aber nicht kompensieren muss.
Zan Lynx
@ ZanLynx: Ja, ich bin sicher, dass das richtig ist. Gleichzeitig existiert die Besetzung, was bedeutet, dass jemand, der die Sprache entworfen hat, eine Idee hatte, dass "wir das vielleicht irgendwann brauchen" - was bedeutet, dass es überhaupt nicht dazu gedacht ist, irgendetwas Nützliches zu tun.
Mats Petersson
1

Es ist unmöglich , im Allgemeinen für die Compiler , um zu bestimmen , ob die Variable wird geändert werden, wurde als hingewiesen.

Wenn const-ness Überprüfung, scheint die Frage von Interesse zu sein , wenn die Variable kann durch eine Funktion geändert werden. Auch dies ist in Sprachen, die Zeiger unterstützen, schwierig. Sie können nicht steuern, was anderer Code mit einem Zeiger macht. Er kann sogar von einer externen Quelle gelesen werden (obwohl dies unwahrscheinlich ist). In Sprachen, die den Zugriff auf den Speicher einschränken, können diese Arten von Garantien möglich sein und eine aggressivere Optimierung ermöglichen als C ++.

Krumelur
quelle
2
Eine Sache, von der ich wünschte, sie würde in Sprachen unterstützt, wäre eine Unterscheidung zwischen kurzlebigen, rückzahlbaren und dauerhaften Referenzen (oder Zeigern). Vergängliche Referenzen dürfen nur in andere kurzlebige Referenzen kopiert werden, Mehrwegreferenzen können in kurzlebige oder Mehrwegreferenzen kopiert werden und dauerhafte können auf jede Art und Weise kopiert werden. Der Rückgabewert einer Funktion wird durch das restriktivste der Argumente eingeschränkt, die als "returnable" -Parameter übergeben werden. Ich halte es für bedauerlich, dass in vielen Sprachen, wenn man eine Referenz übergibt, nichts darauf hinweist, wie lange sie verwendet werden kann.
Supercat
Das wäre sicherlich nützlich. Es gibt natürlich Muster dafür, aber in C ++ (und vielen anderen Sprachen) ist es immer möglich zu "betrügen".
Krumelur
Eine wichtige Methode, mit der .NET Java überlegen ist, besteht darin, dass es ein Konzept für eine kurzlebige Referenz hat. Leider gibt es für Objekte keine Möglichkeit, Eigenschaften als kurzlebige Referenzen verfügbar zu machen (was ich wirklich gerne sehen würde, wäre ein Mittel dazu
Welcher
1

Um die Frage genauer zu formulieren, schlage ich vor, dass der Autor des Buches möglicherweise folgende Einschränkungen im Sinn hatte:

  1. Angenommen, der Compiler untersucht das Verhalten einer bestimmten Funktion in Bezug auf die Konstanz einer Variablen. Aus Gründen der Korrektheit müsste ein Compiler (aufgrund des unten erläuterten Aliasing) annehmen, dass die Variable geändert wird, wenn die Funktion eine andere Funktion aufruft. Daher gilt die Annahme Nr. 1 nur für Codefragmente, die keine Funktionsaufrufe ausführen.
  2. Angenommen, die Variable wird nicht durch eine asynchrone oder gleichzeitige Aktivität geändert.
  3. Angenommen, der Compiler bestimmt nur, ob die Variable geändert werden kann, nicht, ob sie geändert wird. Mit anderen Worten, der Compiler führt nur statische Analysen durch.
  4. Angenommen, der Compiler berücksichtigt nur korrekt funktionierenden Code (ohne Berücksichtigung von Array-Über- / Unterläufen, fehlerhaften Zeigern usw.).

Im Zusammenhang mit dem Compiler-Design halte ich die Annahmen 1,3,4 aus Sicht eines Compiler-Writers im Kontext der Code-Gen-Korrektheit und / oder Code-Optimierung für absolut sinnvoll. Annahme 2 ist sinnvoll, wenn das Schlüsselwort volatile nicht vorhanden ist. Und diese Annahmen fokussieren auch die Frage genug, um die Beurteilung einer vorgeschlagenen Antwort viel definitiver zu machen :-)

Angesichts dieser Annahmen ist ein Hauptgrund, warum Konstanz nicht angenommen werden kann, das variable Aliasing. Der Compiler kann nicht wissen, ob eine andere Variable auf die Variable const zeigt. Aliasing kann auf eine andere Funktion in derselben Kompilierungseinheit zurückzuführen sein. In diesem Fall kann der Compiler Funktionen durchsuchen und mithilfe eines Aufrufbaums statisch bestimmen, ob Aliasing auftreten kann. Wenn das Aliasing jedoch auf eine Bibliothek oder einen anderen Fremdcode zurückzuführen ist, kann der Compiler bei der Funktionseingabe nicht feststellen, ob Variablen ein Aliasing aufweisen.

Sie könnten argumentieren, dass eine Variable / ein Argument, die als const markiert ist, nicht durch Aliasing geändert werden sollte, aber für einen Compiler-Writer ist das ziemlich riskant. Es kann für einen menschlichen Programmierer sogar riskant sein, eine Variable const als Teil eines großen Projekts zu deklarieren, bei dem er das Verhalten des gesamten Systems oder des Betriebssystems oder einer Bibliothek nicht kennt, um eine gewonnene Variable wirklich zu kennen. ' t ändern.

Χpẘ
quelle
0

Selbst wenn eine Variable deklariert ist const, bedeutet dies nicht, dass schlecht geschriebener Code sie überschreiben kann.

//   g++ -o foo foo.cc

#include <iostream>
void const_func(const int&a, int* b)
{
   b[0] = 2;
   b[1] = 2;
}

int main() {
   int a = 1;
   int b = 3;

   std::cout << a << std::endl;
   const_func(a,&b);
   std::cout << a << std::endl;
}

Ausgabe:

1
2
Mark Lakata
quelle
Dies geschieht, weil aund bStapelvariablen sind und sich b[1]zufällig am selben Speicherort befinden wie a.
Mark Lakata
1
-1. Undefiniertes Verhalten beseitigt alle Einschränkungen des Compilerverhaltens.
MSalters
Unsicher über die Abwahl. Dies ist nur ein Beispiel für die ursprüngliche Frage des OP, warum ein Compiler nicht herausfinden kann, ob etwas wirklich ist, constwenn alles beschriftet ist const. Dies liegt daran, dass undefiniertes Verhalten Teil von C / C ++ ist. Ich habe versucht, einen anderen Weg zu finden, um seine Frage zu beantworten, anstatt das Problem des Stillstands oder externe menschliche Eingaben zu erwähnen.
Mark Lakata
0

Um meine Kommentare zu erweitern, ist der Text dieses Buches unklar, was das Problem verschleiert.

Wie ich kommentierte, versucht dieses Buch zu sagen: "Lassen Sie uns eine unendliche Anzahl von Affen dazu bringen, jede denkbare C ++ - Funktion zu schreiben, die jemals geschrieben werden könnte. Es wird Fälle geben, in denen wir eine Variable auswählen, die (eine bestimmte Funktion, die die Affen geschrieben haben) verwendet, können wir nicht herausfinden, ob die Funktion diese Variable ändern wird. "

Natürlich kann dies für einige (sogar viele) Funktionen in einer bestimmten Anwendung vom Compiler sehr einfach bestimmt werden. Aber nicht für alle (oder unbedingt für die meisten).

Diese Funktion kann einfach so analysiert werden:

static int global;

void foo()
{
}

"foo" ändert "global" eindeutig nicht. Es ändert überhaupt nichts, und ein Compiler kann dies sehr einfach herausfinden.

Diese Funktion kann nicht so analysiert werden:

static int global;

int foo()
{
    if ((rand() % 100) > 50)
    {
        global = 1;
    }
    return 1;

Da die Aktionen von "foo" von einem Wert abhängen, der sich zur Laufzeit ändern kann , kann zur Kompilierungszeit offenbar nicht festgestellt werden , ob "global" geändert wird.

Dieses ganze Konzept ist viel einfacher zu verstehen, als es Informatiker vermuten. Wenn die Funktion etwas anderes tun kann, basierend darauf, dass sich die Dinge zur Laufzeit ändern können, können Sie nicht herausfinden, was sie tun wird, bis sie ausgeführt wird, und jedes Mal, wenn sie ausgeführt wird, kann sie etwas anderes tun. Ob es nachweislich unmöglich ist oder nicht, es ist offensichtlich unmöglich.

El Zorko
quelle
Was Sie sagen, ist wahr, aber selbst für sehr einfache Programme, für die zur Kompilierungszeit alles bekannt ist, können Sie nichts beweisen, nicht einmal, dass das Programm stoppt. Dies ist das Halteproblem. Zum Beispiel könnten Sie ein Programm schreiben, das auf Hailstone-Sequenzen en.wikipedia.org/wiki/Collatz_conjecture basiert, und es als wahr zurückgeben, wenn es zu einem konvergiert. Compiler werden es nicht können (da es in vielen Fällen überlaufen würde) und selbst Mathematiker wissen nicht, ob es wahr ist oder nicht.
kriss
Wenn Sie meinen "es gibt einige sehr einfach aussehende Programme, für die Sie nichts beweisen können", stimme ich voll und ganz zu. Turings klassischer Halting Problem-Beweis beruht jedoch im Wesentlichen darauf, dass ein Programm selbst erkennen kann, ob es anhält, um einen Widerspruch aufzubauen. Da dies Mathematik ist, keine Implementierung. Es gibt sicherlich Programme, bei denen es zur Kompilierungszeit durchaus möglich ist, statisch zu bestimmen, ob eine bestimmte Variable geändert wird und ob das Programm angehalten wird. Es ist möglicherweise nicht mathematisch beweisbar, aber in bestimmten Fällen praktisch erreichbar.
El Zorko