Was genau habe ich von Pointern?



  • Ohne jetzt den Rest zu kommentieren:

    ShadeOfMine@work schrieb:

    wichtig ist, dass man erkennt dass close() in java ein destruktur aufruf ist. und ein neues open wäre wieder ein ctor aufruf.

    Das ist definitiv falsch. Es gibt im engeren Sinn bei Java gar keine Destruktoren, sondern nur sogenannte Finalizer. Ob das jetzt das gleiche ist oder nicht, wäre eine andere Diskussion. Aber eine Methode close() aufzurufen ist ein ganz normaler Methodenaufruf - der den Objektstatus ändern kann, wie er will. Du spielst wahrscheinlich auf die Java-IO-Klassen an, die größtenteils eine solche Methode haben - diese dient aber nicht zum Zerstören des Objektes, sondern zum Schließen der zugeordneten externen Ressourcen.

    Alles andere hat Simon2 schon gesagt.



  • Wer segt überhaupt, dass Model nicht clonable ist? So wie Dein Beispiel gehalten ist, kann m alles mögliche sein. Nur "hängend" ganz bestimmt nicht.



  • Simon2 schrieb:

    A* a;
    {
       A b;
       a = &b;
    }
    a->methode(); // undefiniert
    

    Easy Cheesy:

    A a;
    {
      A b=new A();
      a=b;
      b.close();
    }
    a.methode(); // Exception
    

    Jetzt wirst du sagen in Java kann man ein

    if(a.isOpen())
    

    machen - aber genauso kann ich in C++ ein

    if(IsPtrValid(a))
    

    Mal von anderen Techniken abgesehen. Das lustige ist, es passiert bei beiden Codes das selbe: es tritt eine "Illegal State" Situation ein 😉

    In C++ kann man hier super Hardware Exception stattdessen fangen, wenn du soviel wert auf exception legst.



  • PS:
    Wer finalizer mit Destruktoren gleichsetzt und nicht close Methoden, der hat etwas grundlegendes in Java nicht verstanden...

    Und es gibt keine Diskussion was mein Beispielcode macht. Ich hab es gesagt was er macht. Und getModel clont nicht - könnt ihr überhaupt Java? Schonmal in Java programmiert? Oder rede ich hier mit Leuten die keine Ahnung von Java haben?

    Kommt mir nämlich so vor... Also welche Sprache kennt ihr denn? Dann machen wir es in der. Kein Problem.



  • ShadeOfMine@work schrieb:

    Easy Cheesy:

    A a;
    {
      A b=new A();
      a=b;
      b.close();
    }
    a.methode(); // Exception
    

    Das Objekt a exisitert aber noch, immerhin kann es eine Exception werfen. Mit anderen Worten, der Zustand ist genau definiert, was der Unterschied zu C++ ist.

    ShadeOfMine@work schrieb:

    Jetzt wirst du sagen in Java kann man ein

    if(a.isOpen())
    

    machen - aber genauso kann ich in C++ ein

    if(IsPtrValid(a))
    

    Wie soll denn IsPtrValid aussehen? Ein wilder Pointer hat ja kein spezielles aussehen, sonst wär er nicht wild.

    ShadeOfMine@work schrieb:

    Mal von anderen Techniken abgesehen. Das lustige ist, es passiert bei beiden Codes das selbe: es tritt eine "Illegal State" Situation ein 😉

    Der Unterschied ist, dass in Java diese "Illegal State" genau definiert ist, es wird eine Exception geworfen. In C++ kann alles passieren.

    Ich hab eher wenig Ahnung von Java, aber so weit ich weiß, werden Objekte erst dann vom GC weggeräumt, wenn es keine Referenz mehr darauf gibt.
    Da ein wilder Pointer ein Pointer auf ein Speicherbereich ist, in dem nichts definiertes mehr vorzufinden ist, kann es ihn in Java nicht geben. Solange ich den Pointer habe, kann das Objekt davon nicht nicht exisiteren. Klar kann es eine Exception auswerfen, weil es im Kontext der Programmlogik nicht mehr funktioniert, aber auf Sprachebene ist es ein valides Objekt.



  • Du bestätigst mich:

    ShadeOfMine@work schrieb:

    Simon2 schrieb:

    ...a->methode(); // undefiniert
    

    ...

    a.methode(); // Exception
    

    Eben: Exception != undefiniert

    ShadeOfMine@work schrieb:

    ...
    machen - aber genauso kann ich in C++ ein

    if(IsPtrValid(a))
    

    Nein - eben nicht.

    ShadeOfMine@work schrieb:

    ...In C++ kann man hier super Hardware Exception stattdessen fangen, ...

    Nein, kann man eben NICHT !!!
    Denn es muss keine Hardware-, OS- oder sonstige Exception fliegen!!
    Es kann genausogut

    • methode() erfolgreich auf korrekten Daten oder
    • methode() erfolgreich auf falschen Daten oder
    • methode() nicht erfolgreich (verändert anderes als gedacht) auf korrekten Daten oder
    • methode() "irgendwie" oder
    • eine andere Memberfunktion eines ganz anderen Objekts einer anderen Klassen ausgeführt werden,
    • ganz anderer Code durchlaufen werden,
    • das Betriebssystem abrauchen,
    • die Platte formatiert werden,
    • ...
    • und das jeweils je nach "Tageslaune des Systems" (also bei jedem Durchlauf was Anderes)

    Das versteht man unter "undefiniert" .... und genau DA liegt das Problem.
    (mir gehen langsam die Beschreibungsalternativen von "undefiniert" aus)

    ShadeOfMine@work schrieb:

    PS:
    Wer finalizer mit Destruktoren gleichsetzt ...

    Habe ich nicht gemacht, wie Dir anscheinend entgangen sein dürfte. Ich habe lediglich gesagt, ab wann in der jeweiligen Sprache das "Lebensende" eines Objekts definiert ist.

    Gruß,

    Simon2.

    P.S.: Lass doch bitte die vollkommen überflüssigen "Ihr-habt-ja-alle-keine-Ahnung"-Ausflüge. Bringt keinem etwas ...



  • ShadeOfMine@work schrieb:

    aber in der tat - das marketing von java/.net ist nicht schlecht: ein programm ohne absturz hat keine fehler.

    Das Marketing von Java und .NET lügt nicht. Niemand sagt, daß Java-Programme nicht abstürzen können. Das können sie sehr wohl, oft mit der allseits bekannten Nullpointer-Exception. Was sie aber nicht können ist, mit wilden Pointern unkontrolliert im Speicher wüten, weil es dort keine wilden Pointer geben kann.

    ShadeOfMine@work schrieb:

    das gefährliche an den verwaisten referenzen in c#/java/... ist aber eben dass das programm _nicht_ abstürtzt. es läuft fehlerbehaftet weiter.

    Jedes System und jede Programmiersprache hat ihre eigenen Fehlermöglichkeiten. Wie Java ausschweifende Pointer-Manipulation verhindert, ist vielleicht nicht perfekt, aber ein großer Vorteil im Vergleich zu Sprachen wie C. Du solltest vielleicht auch mal daran denken, daß Java und C für komplett andere Anwendungsgebiete geeignet sind. C setzt auf Geschwindigkeit und kleine Binaries. Pointer müßen deshalb ohne Runtime-Checks in direkte Speicherzugriffe übersetzt werden. C wird daher oft in Embedded Systemen, für Systemprogrammierung und für Treiber eingesetzt. In C ist dem Programmierer nahezu alles erlaubt. C geht davon aus, daß mündige und talentierte Leute Programme schreiben. Im Gegensatz dazu erlaubt Java bereits mittelmäßig erfahrenen Programmierern aufwendige Programme zu schreiben, ohne daß sie alle 5 Zeilen in eine "Undefined Behavior" oder "Dangling Pointer" -Falle tapsen.

    ShadeOfMine@work schrieb:

    solche fehler gibt es. die gibt es in c++ ja auch. die gibt es überall wo man zeiger hat. weil immer wenn ich referenzen auf objekte habe (ids sind nichts anderes als eine referenz auf ein objekt - oder zB die hashkeys in einer hash map) kann sich das objekt ändern ohne dass ich es erwarte.

    Du vergleichst Äpfel mit Birnen. Handles, IDs, usw. sind abstrahierte Verweise auf Objekte, über die das Programm (oder die VM) absolute Kontrolle hat und die jederzeit validiert werden können. Wohingegen "echte" Pointer, wenn sie erstmal losgelassen und verwildert sind, sich jeder Einflußnahme entziehen. Beispiel: Versuch doch mal einen Nullpointer-Zugriff mit einer C++ Exception zu fangen.



  • Ist es nicht eigentlich auch eine Art "undefiniertes Verhalten" für das Programm, wenn es nicht das macht, was vorgesehen ist, eben durch das Arbeiten auf einer veralteten Referenz?



  • Badestrand schrieb:

    Ist es nicht eigentlich auch eine Art "undefiniertes Verhalten" für das Programm, wenn es nicht das macht, was vorgesehen ist, eben durch das Arbeiten auf einer veralteten Referenz?

    Man könnte sagen, es gibt 2 Ebenen für undefiniertes Verhalten.
    Einmal die Programmlogik: Hier kann es durchaus undefiniert sein, was passiert, wenn ich mit einem veralteten Objekt arbeite.
    Dann gibts noch die (Programmier-)Sprachebene. In C++ ist es undefiniert, was passiert, wenn ich auf freigegebenen (oder nicht initialisierten) Speicher zugreife. In Java kann das nicht passieren, hier ist das verhalten bei einem Referenzzugriff immer genau definiert, in dem Sinne, dass die Programm Logik zum tragen kommt.



  • Badestrand schrieb:

    Ist es nicht eigentlich auch eine Art "undefiniertes Verhalten" für das Programm, wenn es nicht das macht, was vorgesehen ist, ...?

    Ganz einfach: Nein.

    Das mag unerwartetes Verhalten sein, aber undefiniert ist eben etwas anderes (weil es nichts mit der Erwartungshaltung des Programmierers oder Anwenders zu tun hat).

    Ein einfaches Gegenbeispiel: Bei undefiniertem Verhalten kann der Programmlauf auch das (vom Benutzer) erwartete Verhalten ergeben.
    Ich glaube auch nicht, dass hier der neu eingeführte Begriff "veraltete Referenz" weiterhilft.

    Gruß,

    Simon2.



  • DerKuchen schrieb:

    ...
    Einmal die Programmlogik: Hier kann es durchaus undefiniert sein, was passiert, wenn ich mit einem veralteten Objekt arbeite.
    ...

    Ich halte da nicht viel von, zwanghaft unterschiedliche Dinge unter denselben Begriff "hinzudefinieren" (was man schon daran sieht, dass plötzlich eine vorher unerwähnte "veraltete Referenz" auftaucht). Was Du ansprichst mag, wie gesagt, unerwartet, unerwünscht, fachlich falsch, .... sein - undefiniert ist es nicht.

    Das "close-Beispiel" hätte z.B. in der folgenden Form exakt dasselbe Ergebnis:

    A a;
    A b=new A();
    b.close(); // erst close() ...
    a=b;       // ... dann Zuweisung
    a.methode(); // Exception
    

    ... und wer würde da von undefiniertem Verhalten sprechen (dem Programmierer sollte das Verhalten nicht einmal unerwartet sein) ?

    Gruß,

    Simon2.



  • Simon2 schrieb:

    Das mag unerwartetes Verhalten sein, aber undefiniert ist eben etwas anderes (weil es nichts mit der Erwartungshaltung des Programmierers oder Anwenders zu tun hat).

    Lustig ist, dass sehr wohl immer definiert ist was passieren wird. Lediglich undefiniert heisst, dass der Standard nicht vorschreibt was zu passieren hat 😉

    Aber ein Computer macht ja nicht Sachen zum Spaß, es ist genau definiert was passiert - wenn nicht, dann wäre das ja ein ziemliches Problem.

    zB fliegt bei einem Zugriff auf Speicher der mir nicht gehört eine Hardware Exception. Das ist definiert. Nur eben nicht im C++ Standard, deshalb sagt man dass es undefiniert sei.

    Insofern ist es etwas lustig von unerwarteten vs undefiniertem verhalten zu sprechen 😉

    Deshalb ist dein Code auch so lächerlich, da in C++ eine Hardware statt einer Software Exception fliegt - der Code aber gleich bleibt. Aber ja, Java hat Magie und da ist alles anders 😉



  • Shade Of Mine schrieb:

    zB fliegt bei einem Zugriff auf Speicher der mir nicht gehört eine Hardware Exception. Das ist definiert. Nur eben nicht im C++ Standard, deshalb sagt man dass es undefiniert sei.

    Ist es definiert, was beim Zugriff auf eine lokale Variable, die nicht mehr existiert, aber noch referenziert wird (z.B. nach einem return), geschieht? Es ist klar definiert, dass der Zugriff *irgendwo* im Stack landet. Doch ob der Bereich noch dem Prozess gehört, was für Daten dort stehen, hängt doch von dem ab, was seit dem Löschen der Variablen geschehen ist. Eine Hardware Exception wirst Du hier nie zu sehen bekommen, da auf den meisten Architekturen der Stack über die gesamte Laufzeit konstant ist.

    Ansonsten muss ich Simon2 mal beipflichten, Du solltest nicht so oft mit "lächerlich" und "habt Ihr keine Ahnung" etc. um Dich werfen. Diskussionen führt man, um von den Erkenntnissen anderer zu profitieren, und nicht um anderen die eigenen Erkenntnisse aufzuzwingen.



  • LordJaxom schrieb:

    Ist es definiert, was beim Zugriff auf eine lokale Variable, die nicht mehr existiert, aber noch referenziert wird (z.B. nach einem return), geschieht? Es ist klar definiert, dass der Zugriff *irgendwo* im Stack landet. Doch ob der Bereich noch dem Prozess gehört, was für Daten dort stehen, hängt doch von dem ab, was seit dem Löschen der Variablen geschehen ist. Eine Hardware Exception wirst Du hier nie zu sehen bekommen, da auf den meisten Architekturen der Stack über die gesamte Laufzeit konstant ist.

    Tja, wenn wir jetzt unbedingt von einem Stack reden - der ist übrigens nicht konstant sondern wächst 😉 Aber davon mal abgesehen passiert immer das selbe wenn ich exakt das selbe mache.

    Das ist der Sinn eines Computers



  • Shade Of Mine schrieb:

    Tja, wenn wir jetzt unbedingt von einem Stack reden - der ist übrigens nicht konstant sondern wächst

    Ja, tun wir - oder wie auch immer das, wo das Objekt aus Deinem "Easy Cheezy" Beispiel abgelegt wird, heißt. Und dass der Stack technisch wächst, weiss ich, ändert aber nichts daran dass der Speicherbereich, in dem der Stack wächst, von Anfang an dem Prozess gehört (wegen der Hardwareexception).

    Aber davon mal abgesehen passiert immer das selbe wenn ich exakt das selbe mache.

    Das ist der Sinn eines Computers

    Natürlich. Aber das Auftreten eines bestimmten Problems führt bei unterschiedlichen Rahmenbedingungen nicht zum selben Ergebnis. Irgendwo müssen wir bei undefiniert ansetzen, sonst können wir das Wort aus dem Standard streichen.



  • Shade Of Mine schrieb:

    Tja, wenn wir jetzt unbedingt von einem Stack reden - der ist übrigens nicht konstant sondern wächst 😉

    Der Stack wächst nicht. Er ist konstant. Nur seine Belegung wächst, bis das Maximum erreicht ist. Du wiedersprichst Dir, was das betrifft, übrigens selbst in Deinen obigen Ausführungen.

    Shade Of Mine schrieb:

    Aber davon mal abgesehen passiert immer das selbe wenn ich exakt das selbe mache.
    Das ist der Sinn eines Computers

    Wieder falsch. Wenn Du z.B. mit nicht initialisierten Variablen arbeitest, kann es zu unterschiedlichem Verhalten führen, obwohl Du selbst immer das selbe machst.
    Es gibt Schlüsselgenerierungsverfahren, die genau auf dieser Grundlage eine sehr starke Diversität in den Schlüsseln haben, bzw. diese verlieren, wenn sie weg"optimiert" wird (Gabs gerade erst).

    Ich finde es lustig, dass Du immer wieder dazu neigst, gängige Definitionen und Festlegungen für ungültig zu erklären und Deine eigenen Ansichten über Dinge als das einzig Richtige darstellst.
    Das war beim Thema Reflektion im "Rund um die Programmierung"-Forum auch schon der Fall. Nur, weil Du Deine eigenen falschen Ansichten immer gebetsmühlenartig wiederholst, werden sie nicht richtig dadurch.

    Und Deine Hardware Exception mag ja toll definiert sein, auf x86 oder 68000 basierenden Architekturen. Aber auf 'nem AD (T)SHARC oder 'nem TMS xy sieht das schon wieder ganz anders aus. Der C++ Standard sieht aber diverse Plattformen vor, also auch welche, auf denen Zugriffe auf ungültigen Speicher keine Exception auslösen können.

    Und Exceptions (also Ausnahmefälle/unerwartetes) mit undefiniertem Verhalten gleichzusetzen ist auch blödsinn, weil das eine per Definition vom Laufzeitsystem erkannt und behandelt werden kann, das andere aber einfach irgendentwas tut, dem nicht entgegen gewirkt werden kann.

    Und wer mit so genialen Ideen wie IsPtrValid(p) hier aufschlägt, sollte lieber anderen Leuten keine Ahnungslosigkeit vorwerfen sondern erstmal über seine eigenen Kenntnisse nachdenken.



  • LordJaxom schrieb:

    Shade Of Mine schrieb:

    Tja, wenn wir jetzt unbedingt von einem Stack reden - der ist übrigens nicht konstant sondern wächst

    Ja, tun wir - oder wie auch immer das, wo das Objekt aus Deinem "Easy Cheezy" Beispiel abgelegt wird, heißt. Und dass der Stack technisch wächst, weiss ich, ändert aber nichts daran dass der Speicherbereich, in dem der Stack wächst, von Anfang an dem Prozess gehört (wegen der Hardwareexception).

    Unter Windows nicht. Windows weist dem Stack nur wenige Pages zu, und immer wenn der Stack über den bereich einer Page hinauswächst, wird eine Kernelexception geworfen, die dann eine neue Page freigibt(natürlich nur bis zur maximalgröße). Da diese Exception nur genau einmal pro Page geworfen wird, kann man sich lustig seinen Stack killen, wenn man vorzeitig versucht auf den Speicherbereich zuzugreifen.



  • DerKuchen schrieb:

    Man könnte sagen, es gibt 2 Ebenen für undefiniertes Verhalten.

    Genau darauf wollte ich hinaus; und Shade argumentiert (imho), dass das "unerwartete" Verhalten auf Programmebene nicht besser ist, als das "undefinierte" Verhalten auf Mikro-Ebene, Simon2 argumentiert (imho) genau andersrum, deshalb wird da wahrscheinlich kein gemeinsamer Nenner zustande kommen 🙂

    Simon2 schrieb:

    Badestrand schrieb:

    Ist es nicht eigentlich auch eine Art "undefiniertes Verhalten" für das Programm, wenn es nicht das macht, was vorgesehen ist, ...?

    Das mag unerwartetes Verhalten sein, aber undefiniert ist eben etwas anderes (weil es nichts mit der Erwartungshaltung des Programmierers oder Anwenders zu tun hat).

    Das sehe ich anders, eben weil es durchaus etwas mit der Erwartungshaltung des Programmierers zu tun hat. Wenn ich auf alten Daten hantiere (was ja auch nicht zwingend deterministisch geschieht sondern von sonstwas-Faktoren abhängen kann), dann tut das Programm etwas anderes, als womit ich rechne. Und ich denke nicht, dass unbedingt auch jedesmal eine Exception geworfen wird.
    Und ich erwarte ja in bestimmten Situationen, dass sich meine Software nach einem bestimmten Schema verhält. Zu einem bestimmten Zeitpunkt gehe ich davon aus, dass ein bestimmter Zustand herrscht. Wenn dies wider Erwarten (unerwartet) nicht der Fall sein sollte, dann ist das Verhalten des Programms damit für mich "undefiniert", eben weil ich keine Ahnung hab, was es tun wird.



  • Badestrand schrieb:

    [...]
    Und ich erwarte ja in bestimmten Situationen, dass sich meine Software nach einem bestimmten Schema verhält. Zu einem bestimmten Zeitpunkt gehe ich davon aus, dass ein bestimmter Zustand herrscht. Wenn dies wider Erwarten (unerwartet) nicht der Fall sein sollte, dann ist das Verhalten des Programms damit für mich "undefiniert", eben weil ich keine Ahnung hab, was es tun wird.

    Eigentlich sind undefinierte Dinge immer Programmierfehler. Man programmiert was zusammen, was nicht durch die Sprache oder die Laufzeitumgebung abgedeckt wird.
    Unerwartete Dinge sind aber was anderes:
    Man implementiert eine an sich korrekte Logik. Ein klassisches Beispiel hierfür ist File-IO. Die Erwartungshaltung hierbei ist ein korrekt benutzbarer Datenträger. Nun kann es sich dabei aber z.B. um ein nicht eingelegtes Wechselmedium handeln. Oder der vom Benutzer vorgegebene Dateiname existiert nicht. Hierbei handelt es sich dann um eine Ausnahme, welche die ansich korrekte Logik nicht berücksichtigt. Jedoch sind die Funktionen zum Filezugriff i.d.R. so ausgelegt, dass sie melden können, dass was nicht stimmt (also Ausnahmen eingetreten sind). Die Behandlung der Ausnahme obligt dem Anwender der Funktion.



  • Badestrand schrieb:

    ...

    Simon2 schrieb:

    Badestrand schrieb:

    Ist es nicht eigentlich auch eine Art "undefiniertes Verhalten" für das Programm, wenn es nicht das macht, was vorgesehen ist, ...?

    Das mag unerwartetes Verhalten sein, aber undefiniert ist eben etwas anderes (weil es nichts mit der Erwartungshaltung des Programmierers oder Anwenders zu tun hat).

    Das sehe ich anders, eben weil es durchaus etwas mit der Erwartungshaltung des Programmierers zu tun hat. ...

    Nö.
    Natürlich kann das Programmieren eines undefinierten Ablaufs dazu führen, dass sich das Programm (für den Programmierer) unerwartet verhält ... aber ich habe z.B. oben ein Programm geschrieben, von dem ich zurecht erwarte, dass es sich undefiniert verhält. Es ist immer undefiniert, egal, was ich erwarte.
    Andersherum kann ein durchaus definierter Ablauf für mich unerwartet sein (ist sogar ein sehr häufiges Phänomen - wie Shade schon zur genüge angesprochen hat).
    Wir halte fest: Es gibt Verhalten, dass

    • undefiniert und unerwartet,
    • definiert und unerwartet,
    • undefiniert und erwartet

    ist.

    Ergo: Unerwartet und undefiniert sind nicht nur unterschiedliche Begriffe, sondern bezeichnen auch unterschiedliche Dinge.

    Gruß,

    Simon2.


Anmelden zum Antworten