Pointer wegen möglichen Stackoverflow?



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

    Auch hier fällt mir auf Anhieb kein Problem ein, bei dem das bei auf ausgewachsenen PCs üblichen Stack-Größen zum Problem wird

    Ich bin einmal an die Stackgrenze gestoßen, als ich eine Dreiecksvermaschung aufbaute und an einen Speziallfall stieß. Ich meine die Dreiecksvermaschung bestand aus über 100000 Dreicke und 99% der Dreiecke lagen in 1% der Fläche. Die Rekursionstiefe dürfte so bei cirka 30000 betragen haben.

    Da der Algo aber soweit optimiert war, habe ich einfach die Stackgröße auf 2 MByte gesetzt.



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

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

    Auch hier fällt mir auf Anhieb kein Problem ein, bei dem das bei auf ausgewachsenen PCs üblichen Stack-Größen zum Problem wird

    Ich bin einmal an die Stackgrenze gestoßen, als ich eine Dreiecksvermaschung aufbaute und an einen Speziallfall stieß. Ich meine die Dreiecksvermaschung bestand aus über 100000 Dreicke und 99% der Dreiecke lagen in 1% der Fläche. Die Rekursionstiefe dürfte so bei cirka 30000 betragen haben.

    Interessant. Ich weiss schon warum ich es erstmal vorsichtig mit "fällt mir auf Anhieb keins ein" formuliert habe. Jetzt wo ich etwas mehr drüber nachdenke, kann ich mir doch ein paar Fälle vorstellen. Besonders auf Graphen. Man stelle sich z.B. einen Binären Baum vor, den man rekursiv abschreiten will, aber der Baum ist derart unbalanciert, dass er de facto eine verkettete Liste ist. Dann wäre die Rekursionstiefe gleich der Anzahl der Knoten - das kann dann schon schnell zu viel werden.

    Oder der Input ist fehlerhaft. Jetzt erinnere ich mich noch an einen Algo, der nur auf zyklenfreien Graphen funktionierte, weil er ansonsten zu einer Endlosrekursion führte. Da habe ich dann eine Zyklenerkennung mit ordentlicher Fehlermeldung eingebaut, da natürlich ein Crash wegen Stack Overflow inakzeptabel war.

    Ich nehme das also zurück, auch wenn ich denke, dass sowas doch eher selten vorkommt. Jedenfalls nicht oft genug, um die Heap-Allokationen für alles zu rechtfertigen (die bei der o.g. Endlosrekursion dann auch vor die Wand gelaufen wären, wenn auch vielleicht mit Out of Memory).



  • @Finnegan Listen oder Bäume aus unique_ptr sind auch so eine Sache. Die können einem im Destruktor schnell um die Ohren fliegen, weil die einzelnen Pointer da sich rekursive freigeben.

    Aber, wo ich gerade unique_ptr schreibe, @Helmut-Jakoby warum shared_ptr. Ich bin ein großer Freund von klarer Ownership und weiß tatsächlich nicht, wann ich das letzte mal eine shared ownership gebraucht habe.

    @KK27 In meinem Verständniss von "modernem" C++, möchte man Value Semantics haben, die STL bietet dafür jede Menge Helfer, wie std::variant, std::optional und so weiter. Das macht, meiner Meinung nach, Code einfacher zu lesen und zu verstehen.
    Alles in einen Smart Pointer zu packen, widerspricht dem Verständnis.

    Als Anregung zu modernem C++ und Value Semantics vlt ein Vortrag von Klaus Iglberger von der CppCon 2022: https://www.youtube.com/watch?v=G9MxNwUoSt0


  • Gesperrt

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

    Auch hier fällt mir auf Anhieb kein Problem ein, bei dem das bei auf ausgewachsenen PCs üblichen Stack-Größen zum Problem wird

    Dito. Für gewöhnlich wird dann eher die Laufzeit zum Problem.

    Wie groß ist denn ein "Element", und wie viele gibt es?



  • Erst mal vielen Dank für die vielen Rückmeldungen.

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

    @KK27 In meinem Verständniss von "modernem" C++, möchte man Value Semantics haben, die STL bietet dafür jede Menge Helfer, wie std::variant, std::optional und so weiter. Das macht, meiner Meinung nach, Code einfacher zu lesen und zu verstehen.
    Alles in einen Smart Pointer zu packen, widerspricht dem Verständnis.

    std::variant und std::optional kann ich leider nicht verwenden. Wenn ich mich nicht täusche ist unser Compiler ein 98er Compiler mit experimentiellen Featues wie Smart Pointern. Nicht alles was in C++ 11 verfügbar ist, können wir verwenden. Aber vieles wie Smart Pointer. Wir wollten unseren Compiler mal updaten, aber da gibt es Probleme mit uralten Bibliotheken und natürlich mit der Zeit. Nur mal so am Rande erwähnt.

    Zum Beispiel habe ich Klasse(n) für ein Projekt geschrieben, die Daten aus einer SQL Datenbank laden. Daten die in der Datenbank den Wert NULL haben können, sind bei mir unique_ptr. Entweder haben sie einen Wert oder wenn sie NULL sind, dann ist es ein nullptr. Gerne hätte ich std::optional in diesem Kontext verwendet. Etwas besseres ist mir damals nicht eingefallen.

    In meinem ganzen Leben als Programmierer ist mir einmal ein Stackoverflow passiert. Es war eine Endlosrekursion.

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

    Ich nehme das also zurück, auch wenn ich denke, dass sowas doch eher selten vorkommt. Jedenfalls nicht oft genug um, die Heap-Allokationen für alles zu rechtfertigen (die bei der o.g. Endlosrekursion dann auch vor die Wand gelaufen wären, wenn auch vielleicht mit Out of Memory).

    Das sehe ich genauso. Das ist ein Sonderfall und deswegen sollte man nicht seinen ganzen Code umbauen.

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

    Das ist doch gerade ein Paradebeispiel dafür, wo der Blob sich darum gefälligst selber zu kümmern hat. Er hat ja auch kaum eine andere Wahl, denn entweder hält er seine Nutzdaten dynamisch, dann ist es sowieso auf dem Heap. Oder er hat ein char[1000000000] als Member in der Hoffnung, dass das für alle Blobgößen reicht, und dann gehört der Code weggeworfen und der Autor ins Gefängnis.

    Naja. Einer unserer Senjor Programmierer verwenden immer noch solche Konstrukte und meinte bei Smart Pointer hat man keine Kontrolle wann es gelöscht wird. Egal wie gut man ihm Smart Pointer erklärt hat.



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

    Naja. Einer unserer Senjor Programmierer verwenden immer noch solche Konstrukte und meinte bei Smart Pointer hat man keine Kontrolle wann es gelöscht wird. Egal wie gut man ihm Smart Pointer erklärt hat.

    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).



  • @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.


Anmelden zum Antworten