Wodar Hospur Geschrieben 16. Januar 2010 Geschrieben 16. Januar 2010 (bearbeitet) Hi, beim Versuch einem Kommilitonen zu erklären wieso man nachdem Löschen von Zeigern diese auf 0 setzen soll bin ich auf etwas mir Unerklärbares gestoßen: #include <iostream> #include <string> using namespace std; class Bla { public: string text; Bla(void) { text = string("Hallo"); } void ausgeben(void) { cout << "Hallo" << "\n"; } } int main(void) { Bla* blubb = new Bla(); blubb->ausgeben(); delete blubb; blubb = NULL; blubb->ausgeben(); return 0; } Liefert mir auf einem aktuellen Archlinux mit gcc 4.4.2 als Ergebnis: Hallo Hallo Wenn der Code jetzt auf: #include <iostream> #include <string> using namespace std; class Bla { public: string text; Bla(void) { text = string("Hallo"); } void ausgeben(void) { cout << text << "\n"; } } int main(void) { Bla* blubb = new Bla(); blubb->ausgeben(); delete blubb; blubb = NULL; blubb->ausgeben(); return 0; } geändert wird gibt es den erwarteten und gewünschten segfault. Aber warum nicht vorher. Ich geh davon aus das es undefiniertes Verhalten ist. Wie kann man sonst diesen segfault provozieren, ist ja besser als in fremden Speicher reinzuschreiben, reinzuarbeiten. Bearbeitet 16. Januar 2010 von Wodar Hospur Zitieren
Klotzkopp Geschrieben 16. Januar 2010 Geschrieben 16. Januar 2010 geändert wird gibt es den erwarteten und gewünschten segfault. Aber warum nicht vorher. Ich geh davon aus das es undefiniertes Verhalten ist.Es ist undefiniertes Verhalten. Du kannst hier schlicht und einfach nicht erwarten, dass etwas bestimmtes passiert. Sich zu fragen, warum Code mit "undefined Behavior" in diesem oder jenem Fall dieses oder jenes Verhalten zeigt, ist sinnlos. Wie kann man sonst diesen segfault provozieren, ist ja besser als in fremden Speicher reinzuschreiben, reinzuarbeiten.Sorg dafür, dass so etwas gar nicht erst passieren kann. Wenn du denn unbedingt mit Zeigern arbeiten musst, dann nimm Smartpointer. beim Versuch einem Kommilitonen zu erklären wieso man nachdem Löschen von Zeigern diese auf 0 setzen sollDas darfst du mir aber bitte auch mal erklären. Mir fällt dafür nämlich kein vernünftiger Grund ein. Zitieren
Wodar Hospur Geschrieben 16. Januar 2010 Autor Geschrieben 16. Januar 2010 Es ist undefiniertes Verhalten. Du kannst hier schlicht und einfach nicht erwarten, dass etwas bestimmtes passiert. Sich zu fragen, warum Code mit "undefined Behavior" in diesem oder jenem Fall dieses oder jenes Verhalten zeigt, ist sinnlos. Naja wenn ich nen Zeiger auf Null setze sollte das einfach nicht passieren. Ich frag mich sogar eher warum es undefiniertes Verhalten gibt. Er müsste in beiden Fällen einfach sterben. Für mich sieht das irgendwie aus als ob die Funktion static ist und er dann drauf zugreifen würde... Sorg dafür, dass so etwas gar nicht erst passieren kann. Wenn du denn unbedingt mit Zeigern arbeiten musst, dann nimm Smartpointer. Naja aber in einer normalen Einführungsvorlesung ist die boost Lib einfach nicht Bestandteil. Das darfst du mir aber bitte auch mal erklären. Mir fällt dafür nämlich kein vernünftiger Grund ein. Double Free. Sicher ein Problem das mit einer gescheiten Strukturierung zu lösen ist. Aber ein delete auf einen NULL Pointer macht nichts, während ein delete auf eine ungültige Adresse zu nem Absturz oder vielleicht auch nicht definiertem Verhalten führt. Außerdem ist der NULL Pointer bei jeglichen Graphen, Bäumen und Listen ein wichtiger Bestandteil. Zitieren
Klotzkopp Geschrieben 16. Januar 2010 Geschrieben 16. Januar 2010 Naja wenn ich nen Zeiger auf Null setze sollte das einfach nicht passieren. Ich frag mich sogar eher warum es undefiniertes Verhalten gibt.Das Grundkonzept der Sprache ist, dass du für nichts mit Performance bezahlen musst, was du nicht brauchst. Man kann eine Programmiersprache so schreiben, dass bei Zugriffen über Nullzeiger grundsätzlich Laufzeitfehler ausgelöst werden (siehe Java), aber das erfordert eine Prüfung bei jedem einzelnen Zeigerzugriff, das kostet Performance und passt daher nicht ins Sprachkonzept. Wenn du so ein Feature brauchst, kannst du es dir selbst bauen. Er müsste in beiden Fällen einfach sterben. Für mich sieht das irgendwie aus als ob die Funktion static ist und er dann drauf zugreifen würde...Wie gesagt, es ist vom Standpunkt des Standards sinnlos, sich darüber Gedanken zu machen, warum sich dieser Code so oder so verhält. Konkret ist es hier so, dass der this-Zeiger des Objekts als versteckter Parameter übergeben wird, und in diesem Fall eben Null ist. Das geht bei den meisten Compilern so lange gut, wie du nicht auf Membervariablen der Klasse zugreifst, denn nur deren Adressen beziehen sich auf diesen Zeiger. Naja aber in einer normalen Einführungsvorlesung ist die boost Lib einfach nicht Bestandteil. Es muss ja nicht gleich boost sein, einfache Smartpointer kann man sich auch selbst stricken. Und std::auto_ptr gibt's auch noch, auch wenn der ein ziemlich eingeschränktes Anwendungsfeld hat. Double Free. Sicher ein Problem das mit einer gescheiten Strukturierung zu lösen ist.So sehe ich das auch. Ein doppeltes delete ist ein Fehler, den du mit dem Nullsetzen nur vertuschst. Quasi eine Symptombehandlung. Aber ein delete auf einen NULL Pointer macht nichts, während ein delete auf eine ungültige Adresse zu nem Absturz oder vielleicht auch nicht definiertem Verhalten führt.Letzteres. Außerdem ist der NULL Pointer bei jeglichen Graphen, Bäumen und Listen ein wichtiger Bestandteil.Richtig. Aber mal ehrlich, wie oft implementiert man solche Container selbst? Zitieren
Wodar Hospur Geschrieben 16. Januar 2010 Autor Geschrieben 16. Januar 2010 Das Grundkonzept der Sprache ist, dass du für nichts mit Performance bezahlen musst, was du nicht brauchst. Man kann eine Programmiersprache so schreiben, dass bei Zugriffen über Nullzeiger grundsätzlich Laufzeitfehler ausgelöst werden (siehe Java), aber das erfordert eine Prüfung bei jedem einzelnen Zeigerzugriff, das kostet Performance und passt daher nicht ins Sprachkonzept. Wenn du so ein Feature brauchst, kannst du es dir selbst bauen. Joah es geht mir weniger nach dem warum sondern eher nach dem wie.. woher er also das noch auflösen kann. Aber das hast du ja in einem anderen Absatz dann erklärt. Konkret ist es hier so, dass der this-Zeiger des Objekts als versteckter Parameter übergeben wird, und in diesem Fall eben Null ist. Das geht bei den meisten Compilern so lange gut, wie du nicht auf Membervariablen der Klasse zugreifst, denn nur deren Adressen beziehen sich auf diesen Zeiger. Genau das find ich interessant. Das heißt mit dem Typen alleine sind alle Methoden verknüpft. Das sind halt Sprachinternas die garnich so leicht ersichtlich sind. Richtig. Aber mal ehrlich, wie oft implementiert man solche Container selbst? Fast ein halbes Semester lang . Zitieren
Klotzkopp Geschrieben 17. Januar 2010 Geschrieben 17. Januar 2010 Genau das find ich interessant. Das heißt mit dem Typen alleine sind alle Methoden verknüpft. Es geht übrigens in aller Regel nicht mehr, wenn du virtuelle Methoden aufrufst. Denn die sind üblicherweise über einen vtable-Zeiger implementiert, der an der Instanz hängt, also auch relativ zum Instanzzeiger adressiert wird. Das sind halt Sprachinternas die garnich so leicht ersichtlich sind.Es handelt sich weniger um Sprachinterna als um Implementierungsdetails der Compiler. Du bist hier längst über den Bereich hinaus, der durch die Sprache geregelt wird. Zitieren
Xanatus Geschrieben 26. Januar 2010 Geschrieben 26. Januar 2010 Das ganze ist einfach ganz logisch zu erklären. Nachdem dein Programm in Maschienencode umgewandelt (compiliert) wurde, gibt es keine klassen mehr. aus class Bla { public: void ausgeben(void) { cout << text << "\n"; } } wird void ausgeben(void* this) { cout << this->text << "\n"; } und aus blubb->ausgeben(); wird ausgeben(blubb); ist logisch das bei folgendem kein Fehler entsteht, egal was du übergibts, oder? void ausgeben(void* this) { cout << "Hallo" << "\n"; } Das ist kein Fehler, ds ist einfach so ;-) Im Regelfall wird es aber selten vorkommen, dass deine Klasse über Methoden verfügt, die nicht auf den Klassenspeicher zugreifen. HTH, Uwe Zitieren
Empfohlene Beiträge
Dein Kommentar
Du kannst jetzt schreiben und Dich später registrieren. Wenn Du ein Konto hast, melde Dich jetzt an, um unter Deinem Benutzernamen zu schreiben.