Pointer wegen möglichen Stackoverflow?



  • @Helmut-Jakoby sagte in Pointer wegen möglichen Stackoverflow?:

    Wann ich Pointer nutze:
    Aus obigen Gründen und Pointer als Parameter, wenn eine Methode/Funktion ein Objekt als Parameter optional erwartet. Das übergebene Objekt muss natürlich nicht unbedingt im Heap angelegt werden.

    Solche Zeiger verwende ich auch häufiger bzw. als Referenzen wenn die Übergabe nicht optional ist oder das Objekt einen leeren Zustand hat.

    @Finnegan sagte in Pointer wegen möglichen Stackoverflow?:

    Wenn der Zeitpunkt wirklich wichtig ist, dann kann man diesen anhand des Codes oder bei komplizierteren shared_ptr-Strukturen im Debugger exakt bestimmen und ggfs. "verschieben" indem man einfach etwas länger den letzten Pointer hält. Ich wage aber zu behaupten, dass das "wann" eher selten relavant ist, sondern vielmehr, dass der Speicher überhaupt freigegeben wird
    Vielleicht verwechselt der Kollege das "wann" auch mit der Gültigkeit des Objekts hinter dem Pointer. Die garantiert man aber nicht, indem man die Speicherfreigabe selbst kontrolliert, sondern indem man sicherstellt, dass man einen gültigen (Smart-) Pointer auf das Objekt hält, während man darauf zugreift. Da wird nichts freigegeben, solange der existiert (und nicht mit sowas wie ptr.release() explizit freigegeben oder irgendwie gemoved wurde).

    Kann gut sein, dass dies mein Kollege meinte. Mit ein bisschen Übung meistert man das schnell. Am Anfang musste ich mich auch erst mit Smart Pointern anfreunden und lernen damit umzugehen.

    Ich hab jetzt meine Antworten zu dem Thema. Vielen Dank.



  • @KK27 sagte in Pointer wegen möglichen Stackoverflow?:

    Die Frage ist: Wie alt bist Du und wie alt ist der Kollege?

    Es gab früher CPUs am Markt, die nicht den kompletten Adressraum als Stack adressieren konnten. Daher wurde natürlich extrem sparsam mit dem Stack umgegangen, und die Standardeinstellung auf anderen CPUs war auch extrem gering für den Stack. D.h. nicht triviale Allokationen mussten immer auf dem Heap erfolgen. Das setzt sich halt im Kopf fest.


  • Gesperrt

    Na ja, ich kann zumindest für Java sprechen, dass Optionals eher Fluch denn Segen sind. An für sich war das Feature nicht nötig. Aber, in C war's In, also brauchte es Java auch... Wen es interessiert, hier mehr darüber.

    Ansonsten möchte ich mich an dem Kollegen-Bashing nicht beteiligen. Denn jeder weiß ja, dass studierte Leute in der Programmierung nichts taugen usw. Weg mit dem Abschaum. 😅



  • @john-0 sagte in Pointer wegen möglichen Stackoverflow?:

    Die Frage ist: Wie alt bist Du und wie alt ist der Kollege?

    Ich bin 29 Jahre alt. Der Kollege der alles mit Pointern macht und der Kollege der nicht vom unique_ptr überzeugt ist sind beide über 60 Jahren.



  • @KK27 sagte in Pointer wegen möglichen Stackoverflow?:

    Ich bin 29 Jahre alt. Der Kollege der alles mit Pointern macht und der Kollege der nicht vom unique_ptr überzeugt ist sind beide über 60 Jahren.

    Ok, für Dich etwas historischen Kontext, um das besser einordnen zu können, wie man früher Programmieren gelernt hat, da man in der Regel privat keinen Zugriff auf ein VMS System, UNIX System, IBM Mainframe o.ä. hatte.

    Die CPU von Apple II (außer IIGS), Commodore C16/C64/C128 und Atari 8Bit Rechner war entweder direkt ein MOS6502 oder ein Derivat davon. Diese CPU Familie war damals erschwinglich und sehr verbreitet, nur erlaubte diese CPU lediglich 255 Bytes als Stack für alle Routinen die gleichen 255 Bytes. D.h. trotz möglicher 1MB RAM Erweiterung (wahnsinnig teuer und nicht am Stück nutzbar, üblich waren 64kB im II/II+, und 128kB im IIe/IIc) für einen AppleII konnte man nur einen winzigen Stack nutzen. D.h. man nutzte ihn üblicherweise gar nicht.

    Bei anderen 8Bit CPUs (Z80, 8085, …) war der Stack auf 16Bits (64kB) limitiert, selbst wenn per Bank Switching bedeutend mehr RAM verbaut war.

    Damals sehr weit verbreitet waren DOS PCs. Die Intel 8088 CPU war ein Frickelfestival der Extraklasse, somit waren auch DOS und Windows (alles vor NT ist damit gemeint) ein Frickelfestival der Extraklasse. Ein 8086/8088 hat einen Adressraum von 1MB, aber nur wenn man das Indexregister (4Bit) zusätzlich zum Adressregister (16Bit) genutzt wird. Deshalb gibt es unterschiedliche Programmiermodelle mit unterschiedlichen Zeigerngrößen, die unter bestimmten Umständen zusammen genutzt werden. Beim 80286 kommen weitere Modi dazu, die aber unter DOS/Windos nicht genutzt wurden, weil sonst die alten Programme nicht mehr liefen.



  • @john-0 sagte in Pointer wegen möglichen Stackoverflow?:

    Ok, für Dich etwas historischen Kontext, um das besser einordnen zu können, wie man früher Programmieren gelernt hat, da man in der Regel privat keinen Zugriff auf ein VMS System, UNIX System, IBM Mainframe o.ä. hatte.

    Und heute schreiben wir Software für Funkchips in C, deren Programmgröße kleiner ist als 34 kByte, maximal 24 kByte RAM nutzt, mittels Bluetooth LE mit Handy kommuniziert, die Kommunikation zusätzlich Elliptic Curve Digital Signature Algorithm (ECDSA) nutzt, hierbei mehrere Crypto-Libs (Oberon, OPTIGA,..) unterstützt,...



  • ja solche alten sturköpfe, die "das schon immer so gemacht" haben sind echt furchtbar.

    andererseits kann man software, die ja seit 50 jahren "läuft", auch nicht mal eben so neu machen und da sollte man diese alten techniken dann schon beherrschen.


  • Mod

    Das ist ja keine alte Technik, sondern war schon genauso Quark vor 50 Jahren wie heute. Da hat jemand etwas über Heap und Stack gelesen, aber nur gefährliches Halbwissen mitgenommen. Das könnte heute genauso passieren, ist aber trotzdem kein Grund, das zu tolerieren



  • ja natürlich war das schon immer quark, aber ich behaupte jetzt einfach mal, dass es unmengen an code gibt, der genau so erstellt wurde, und dass man dann entweder ganz von vorne anfängt, oder eben weiter so rummurksen muss.

    dass letzteres auf dauer wahrscheinlich günstiger wird und ein in punkto geschwindigkeit und wartbarkeit viel effizienteres programm erzeugt, steht ja außer frage, aber erklär das mal jemandem mit entscheidungsbefugnis. https://www.youtube.com/watch?v=i3TA6-Hzw68

    davon mal ab: war das nicht so, dass man in c++ eigentlich gar keine zeiger mehr verwenden soll, weil die einfach total zum 🤮 sind?


  • Mod

    @Peter-Viehweger sagte in Pointer wegen möglichen Stackoverflow?:

    davon mal ab: war das nicht so, dass man in c++ eigentlich gar keine zeiger mehr verwenden soll, weil die einfach total zum 🤮 sind?

    Was ist denn das Ding, das auf deine dynamisch angeforderten Ressourcen zeigt? Nein, du wirst um Zeiger nicht herumkommen, denn Zeiger können Dinge, die nur Zeiger können.

    Es kann aber gut sein, dass du als high-level Programmierer damit nie in Kontakt kommst, weil eben alle Dinge, die Zeiger brauchen, diese intern benutzen und die Notwendigkeit vor dem Anwender wegkapseln. Das ist ja gerade der Fehler von KK27s Kollegen, dass er meint, das besser zu wissen, und sich damit nur das Leben selber schwerer macht.



  • @SeppJ sagte in Pointer wegen möglichen Stackoverflow?:

    Was ist denn das Ding, das auf deine dynamisch angeforderten Ressourcen zeigt? Nein, du wirst um Zeiger nicht herumkommen, denn Zeiger können Dinge, die nur Zeiger können.

    schon, aber

    Es kann aber gut sein, dass du als high-level Programmierer damit nie in Kontakt kommst, weil eben alle Dinge, die Zeiger brauchen, diese intern benutzen und die Notwendigkeit vor dem Anwender wegkapseln.

    ganz genau das meinte ich: da hat sich schon jemand gedanken gemacht, wie ich ohne zeiger auskomme.

    Das ist ja gerade der Fehler von KK27s Kollegen, dass er meint, das besser zu wissen, und sich damit nur das Leben selber schwerer macht.

    naja nicht nur sich, oder?



  • @Quiche-Lorraine sagte in Pointer wegen möglichen Stackoverflow?:

    Und heute schreiben wir Software für Funkchips in C, …

    Der Unterschied ist nur, dass man heute i.d.R. einen 32Bit Microcontroller nutzt.



  • @Peter-Viehweger sagte in Pointer wegen möglichen Stackoverflow?:

    davon mal ab: war das nicht so, dass man in c++ eigentlich gar keine zeiger mehr verwenden soll, weil die einfach total zum 🤮 sind?

    Ich denke die bessere Empfehlung ist, außerhalb von vielleicht selbst implementierten Containern und Smart Pointern in eigenem Bibliotheks-Code keine besitzenden Zeiger zu verwenden.

    Es kann ja durchaus mal nützlich sein, eine Referenz zu haben, die auch "nicht gesetzt" sein kann. f(T*) kann ich ja schliesslich auch mit einem Smart Pointer als f(ptr.get()) aufrufen, ohne dass dabei ein besitzender Zeiger weitergereicht werden muss.



  • @Finnegan Ich nutze nicht besitzende Pointer zwar auch noch, aber mit std::optional und std::reference_wrapper gibt es da ja auch schon Abstraktionmöglichkeiten, die sowas weg kapseln können.



  • @Schlangenmensch sagte in Pointer wegen möglichen Stackoverflow?:

    @Finnegan Ich nutze nicht besitzende Pointer zwar auch noch, aber mit std::optional und std::reference_wrapper gibt es da ja auch schon Abstraktionmöglichkeiten, die sowas weg kapseln können.

    Kann man machen. Ich frage mich aber, ob das dann aber nicht irgendwann Overkill ist. Solche Objekte sind ja schon etwas umständlicher zu verwenden als ein einfacher Pointer.

    Zugegeben, so ein std::optional wirft natürlich eine Exception, wenn ich auf einen nicht gesetzten Wert zugreifen will. Das ungeprüft zu tun ist aber m.E. ohenhin ein Programmfehler (was ich normal vial assert abfange), der meinentwegen auch mit einem Zugriffsfehler wegen Null-Pointer-Dererferenzierung aussteigen darf (auch wenn das streng genommen UB ist).

    Welche anderen Probleme kann damit vermeiden? Versehentliche oder fehlerhafte Pointer-Arithmetik? So ein T* wäre bei mir jedenfalls immer nur ein Objekt. Wenn ich mehrere haben will, dann verwende ich generische begin/end-Iteratoren oder neuerdings auch Ranges.

    Ich sage nicht, dass std::optional hier schlecht wäre, aber irgendwie will ich da auch nicht ganz auf Pointer verzichten, wegen der simpleren Anwendung. So ein std::optional<std::reference_wrapper<T>> ist schon ganz schön viel "Code-Rauschen", und wie gut das vom Compiler dann tatsächlich in Pointer-äquivalenten Code optimiert wird, müsste ich erstmal noch recherchieren 🤨



  • Im Gegensatz zu einem rohen Zeiger und std::reference_wrapper besitzt std::optional das gehaltene Objekt. Um den Zeiger/Wrapper benutzen zu dürfen muss das Objekt noch irgendwo leben, das ist bei std::optional nicht der Fall. Ein gutes Einsatzgebiet für std::optional sind zB Datenbankfelder, wo der Wert auch mal (null) sein kann, das kann man mit rohen Zeigern oder std::reference_wrapper schlecht modellieren.



  • Ich verfolge sehr interessiert diese Diskussion und habe eine Frage. Welches Konstrukt würde folgende Anforderung ggf. besser erfüllen:
    Eine Methode einer Klasse "ermittelt" ein Objekt (im konkreten Fall ein Objekt aus einer Datenbank) variabler Größe (kann ein Bild unbekannter Größe enthalten) und "liefert" dieses an eine aufrufende Instanz. Es können keine Annahmen getroffen werden, wie lange die aufrufende Instanz das Objekt "braucht".
    In alten Versionen habe ich der liefernden Methode als Parameter eine Referenz auf einen Zeiger geliefert und die liefernde Methode hat ein spezielles Objekt auf dem Heap erstellt und in der Referenz auf den Zeiger zurückgeliefert. In der Doku (in der Hoffnung dass diese gelesen wird 😉 ) habe ich darauf hingewiesen, dass die Verantwortung für das gelieferte Objekt bei der aufrufenden Instanz liegt.
    In den neueren Versionen wird von der liefernden Methode das zu liefernde Objekt in einem std::shared_ptr geliefert und das Problem mit dem zerstören des gelieferten Objektes scheint behoben zu sein.

    Zusätzlich stellt sich mir die Frage; wenn man z.B. mit Bibliotheken wie Qt arbeiten möchte oder muss, ist es da nicht von Vorteil wenn man über die Bedeutung von Zeigern Bescheid weiß?



  • @Helmut-Jakoby

    ich würde ein solches Objekt in Containern der STL anlegen, z.B. als map womit man auch den wahlfreien Zugriff hat. komplexere Resultsets lege ich als Slice in einer Liste an (also eine liste mit maps list < map<string,string> > slice;) eine solche Struktur passt zu allen Template-Engines, also in Hinblick auf eine weitere Verarbeitung und Darstellung der Daten. Weitere Möglichkeiten sind JSON-Strukturen auch dafür gibt es Libraries in C++.

    MFG



  • @SeppJ sagte in Pointer wegen möglichen Stackoverflow?:

    Es kann aber gut sein, dass du als high-level Programmierer damit nie in Kontakt kommst, weil eben alle Dinge, die Zeiger brauchen, diese intern benutzen und die Notwendigkeit vor dem Anwender wegkapseln. Das ist ja gerade der Fehler von KK27s Kollegen, dass er meint, das besser zu wissen, und sich damit nur das Leben selber schwerer macht.

    Meinst du den Kollegen der nicht von Smart Pointern überzeugt ist oder der Kollege der alles mit new und Pointern macht?

    @Peter-Viehweger sagte in Pointer wegen möglichen Stackoverflow?:

    naja nicht nur sich, oder?

    Wenn man Klassen von Kollegen verwendet, die noch mit Char Arrays mit fester Größe arbeiten und diese dann überlaufen, weil sie nicht mit std::string arebiten und das bei neuen Klassen😅 .

    @Peter-Viehweger sagte in Pointer wegen möglichen Stackoverflow?:

    ja solche alten sturköpfe, die "das schon immer so gemacht" haben sind echt furchtbar.

    Die dann solche Argumente auch bei neuen Klassen und Funktionen verwenden und nicht davon abkommen wollen. Man könnte ja etwas kaputt machen oder neue Fehler entdecken. Was früher schon funktioniert hat, funktioniert ja heute auch noch....



  • @Helmut-Jakoby sagte in Pointer wegen möglichen Stackoverflow?:

    ... In alten Versionen habe ich der liefernden Methode als Parameter eine Referenz auf einen Zeiger geliefert und die liefernde Methode hat ein spezielles Objekt auf dem Heap erstellt und in der Referenz auf den Zeiger zurückgeliefert. In der Doku (in der Hoffnung dass diese gelesen wird 😉 ) habe ich darauf hingewiesen, dass die Verantwortung für das gelieferte Objekt bei der aufrufenden Instanz liegt.

    Das ist natürlich "C-Style" und keine gute Idee. Ich finde es jedes mal extrem anstrengend, wie sorgfältig man bei C-Bibliotheken die Doku oder gar den Code lesen muss, gerade wegen solcher Sachen. Das lädt regelrecht zu Fehlern ein. Sowas würde ich zumindest immer als Funktionspaar wie create_object/destroy_object implementieren. Dass der Anwender free ähnliches aufrufen muss, erachte ich da als schlechten Stil, vor allem weil es verschiedene Methoden geben kann, mit denen Speicher verwaltet werden kann (malloc, C++-new, eigene Speicherverwaltung, Cache-Handle, über die Funktionen der Datenbank-API mit speziellen create/destroy-Funktionen, etc.). Mit so einem Funktionspaar behält die Bibliothek die Kontrolle darüber.

    In den neueren Versionen wird von der liefernden Methode das zu liefernde Objekt in einem std::shared_ptr geliefert und das Problem mit dem zerstören des gelieferten Objektes scheint behoben zu sein.

    Ohne den Code zu kennen klingt shared_ptr nach einer guten Lösung. Wenn allerdings nicht erforderlich ist, dass an verschiedenen Stellen im Code eine Referenz auf ein und dasselbe Objekt gehalten werden muss und diese unabhängig voneinander unterschiedlich lange Zugriff darauf benötigen, täte es eventuell auch ein unique_ptr, der noch ein bisschen leichtgewichtiger wäre.

    Zusätzlich stellt sich mir die Frage; wenn man z.B. mit Bibliotheken wie Qt arbeiten möchte oder muss, ist es da nicht von Vorteil wenn man über die Bedeutung von Zeigern Bescheid weiß?

    Selbstverständlich sollte man als C++-Entwickler darüber Bescheid wissen wie Zeiger und auch besitzende Zeiger funktionieren. Wissen immer, aber das so verwenden nur wenn es wirklich einen guten Grund dafür gibt 🙂

    Qt ist diesbezüglich aber auch sehr speziell. Das hat eine automatische Speicherverwaltung, die ich in der Form noch nirgendwo anders gesehen habe. Da sind nackte new ohne delete völlig normal, solange die Objekte in die Qt-Hierarchie "eingehängt" sind. Da werden dann automatisch Kindobjekte feigegeben, wenn Elternobjekte zerstört werden. Das ist ein sehr gewöhnungsbedürftiges System bei dem jedem C++-Entwickler, der das nicht kennt, erstmal die Haare zu Berge stehen würden 🙂 ... das ist aber bei Qt historisch so gewachsen und stammt noch aus einer Zeit vor den C++11-Smartpointern. Duchaus eine Alternative zu unique_ptr/shared_ptr und Verwandten.


Anmelden zum Antworten