[Hilfe!][VS2015]free() (stdlib.h) lässt Programm einfrieren?! (Keine Exception)



  • sizeof(char)==1 (per Definition).
    Mit 1 zu multiplizieren ist genauso gut wie 0 zu addieren 😉



  • Was soll überhaupt die kopiererei mit cpstr ?



  • DirkB schrieb:

    Was soll überhaupt die kopiererei mit cpstr ?

    Es wäre zu vermuten das der Themenstarter denkt, wenn er das char Array mit Realloc erweitert, dass durch diese Erweiterung der String kaputtgeht und er deßhalb eine Kopie vorher abspeichert?
    Anders ergebe es für mich auch nicht wirklich Sinn.

    Aber das wäre auch dahingehend falsch, das realloc natürlich den Speicher beschneiden kann und damit den String kürzen kann wenn die Größe nicht dem String entspricht und kleiner ist.



  • Chris25046 schrieb:

    Die Casts waren testweise, da ich ja partou nicht drauf kam was ihn denn da stört... auch wenn es eigentlich offensichtlich hätte sein müssen.

    partout schreibt man richtig partout und nicht partou.
    Du bist dem Irrtum aller Anfänger aufgesessen, die in C++ programmieren ohne es zu wissen; das sei ja nicht so schlimm, da C++ ja ein Superset von C sei, behaupten sie dann auf Nachfrage.
    Probier mal

    #ifdef __cplusplus
    #error ich programmiere in C++
    #endif
    

    Du bist mit deiner Vermutung der vielgerühmten "Typsicherheit" von C++ aufgesessen, die - zumindest auf C bezogene Kontexte - meint, durch Casting, insbesondere auch Zeigercasts, eine "höhere" Typsicherheit bieten zu können.
    Genau das Gegenteil ist der Fall.
    Mit Casts verlierst du die Original-Typinformation - und was das mit erhöhter Typsicherheit zu tun haben soll, wird dir niemand erklären können - weil das laienhafter Unsinn ist.
    Deswegen - gewöhne dir das von C++ zwingend geforderte Casting bei malloc usw. gar nicht erst an - du gewinnst dadurch nichts sondern verlierst nur - wenn du dir diesen laienhaften Unsinn angewöhnst.
    Casts - insbesondere Zeigercasts - hindern zudem immer den Compiler, dich auf Fehler hinzuweisen zu können.
    Zeigercasts sind nur sehr selten notwendig und nur von Profis sinnvoll einsetzbar, da nur Profis wissen was sie tun, insbesondere wissen sie, was sie sich und dem Compiler damit antun.

    C zwingt dich zu überhaupt nichts (Syntaxregeln mal ausgenommen), C lässt dir alle Freiheiten, weil C von Profis für Profis geschaffen wurde und eben nicht in Anfänger/Professoren/Buchautoren/IT-Berater/IT-Manager/... - Hände gehört.


  • Mod

    Wutz schrieb:

    Du bist mit deiner Vermutung der vielgerühmten "Typsicherheit" von C++ aufgesessen, die - zumindest auf C bezogene Kontexte - meint, durch Casting, insbesondere auch Zeigercasts, eine "höhere" Typsicherheit bieten zu können.
    Genau das Gegenteil ist der Fall.
    Mit Casts verlierst du die Original-Typinformation - und was das mit erhöhter Typsicherheit zu tun haben soll, wird dir niemand erklären können - weil das laienhafter Unsinn ist.

    Verdreh mal nicht die Tatsachen. C++ verbietet bewusst die versehentliche Nutzung der von dir hier zurecht als Profimittel hingestellten Zeigercasts. Dass malloc da auch drunter fällt ist akzeptabler, wahrscheinlich sogar gewünschter, Kollateralschaden, da man malloc in C++ sowieso nie nutzen würde¹.

    Hast natürlich absolut recht, dass man sich selber ins Bein schießt, wenn man zu casten anfängt, um Schutzmechanismen der Sprache auszuhebeln. In C hätte man stattdessen gar nicht erst Casten müssen, in C++ hätte man stattdessen gar nicht erst malloc benutzen sollen. Das Problem entsteht durch die Mischung der Sprachen, oft durch inkompetente Lehrer, die den Unterschied selber gar nicht richtig kennen; oder durch falsch benutzte IDEs.

    ~
    ¹: Ohne diese Fußnote kommt gleich irgendein Klugscheißer und erzählt etwas von eigenen Implementierungen des Operators new~



  • SeppJ schrieb:

    Wutz schrieb:

    [...]
    Mit Casts verlierst du die Original-Typinformation - und was das mit erhöhter Typsicherheit zu tun haben soll, wird dir niemand erklären können - weil das laienhafter Unsinn ist.

    Verdreh mal nicht die Tatsachen. C++ verbietet bewusst die versehentliche Nutzung der von dir hier zurecht als Profimittel hingestellten Zeigercasts. [...]

    Naja, in C ist halt der Vorteil des Typecastings von Pointern die man als Void übergibt, das die Funktion durch ein explizites Typcasting in eine Struktur oder in einen Datentyp selbst entscheiden kann, wie der Parameter auszuwerten ist.

    Ich glaube auch, nichts anderes macht ja auch "printf" mit seinen "Formatspezifikatoren" intern (naiv gesagt).
    Ohne die richtige Formatangabe weiß die Funktion nicht um was für einen Datentyp es sich handelt. Also muß der Typ im Formatstring mit angegeben werden, damit die Funktion weiß wie der Übergabewert zu behandeln ist.

    Genau so wäre dies bei einer Funktion die einen void* Pointer übergeben bekommt, um es generell zu halten, und man eventuell über andere Parameter bestimmt auf was einem "ansprechbaren" Datentyp/Struktur der Pointer wirklich zeigt und wie dieser benutzt werden kann.

    Oder ist die Annahme falsch? Ich lasse mich gern eines besseren Belehren.



  • theSplit schrieb:

    Genau so wäre dies bei einer Funktion die einen void* Pointer übergeben bekommt, um es generell zu halten, und man eventuell über andere Parameter bestimmt auf was einem "ansprechbaren" Datentyp/Struktur der Pointer wirklich zeigt und wie dieser benutzt werden kann.

    Und deshalb sollte auch void* nur sehr selten benutzt werden, eben weil man sich dadurch den Zugriff auf die Original-Typinfo per Definition verbaut und sich diese irgendwo anders her besorgen muss.
    Und genau diese Abwägung - benutze ich void* - und habe eine "universelle" Schnittstelle - muss mir die Typinfo aber irgendwo anders her "besorgen", kann nur ein Profi treffen, idealerweise einer, der nicht nur das Handwerkzeug C perfekt beherrscht sondern auch die (meist umfangreichen) Quellcodes seines Projektes.
    Und die hier noch gar nicht angesprochenen Alignment-Probleme bei Zeigercasts sind ein weiterer Grund, auf solche "universellen" Schnittstellen per void* mit Zeigercasts zu verzichten.



  • SeppJ schrieb:

    Verdreh mal nicht die Tatsachen. C++ verbietet bewusst die

    Ach erzähl mir doch nichts.
    C++ wurde schon seit Beginn als das bessere C propagiert und dabei neben den "überragenden" OOP-Möglichkeiten auch immer wieder die (ggü. C) angeblich bessere bzw. überhaupt erst vorhandene Typsicherheit (Performanzversprechen hat man sich aber dann doch wohlweislich gespart).
    Gleichzeitig hat Stroustrup aber auch so einen Unsinn wie dynamic_cast<> geliefert - Typsicherheit propagieren (oder wie du es formulierst - bewusstes Verbieten von Unsinnskonstrukten) und gleichzeitig standardmäßige Mittel zum Aushebeln derselben anbieten - das ist Verarschung in Reinkultur.
    Warum hat er das denn - völlig ohne Not - getan? Er hatte doch mit dem Erstellen einer völlig neuen Sprache alle Möglichkeiten in der Hand, grundsätzlich saubere OOP anzubieten.
    Nein nein, die gehypte C++-Typsicherheit ist Verarsche - bestenfalls kann man das noch als Marketinggag abqualifizieren.



  • Wutz schrieb:

    Und die hier noch gar nicht angesprochenen Alignment-Probleme bei Zeigercasts sind ein weiterer Grund, auf solche "universellen" Schnittstellen per void* mit Zeigercasts zu verzichten.

    Ich stelle mich gerne doof, was für "Alignment"-Probleme? Ein Pointer ist doch nur 32 bit oder (64 bit) "groß" (je nach Architektur) wenn dieser an eine Funktion übergeben wird?

    In Structs kann ich das vielleicht nachvollziehen, aber der Pointer umfasst trotzdem nur 4 oder 8 Byte und verweist auf eine Speicheradresse eines Datentyps, weiß jedoch nicht in welchen Einheiten die Daten abgespeichert werden müssen? Meinst du jenes?

    Aber ich hatte bisher auch nicht den Anwendungsfall einen Void Pointer in einem Struct zu verwenden, um dann den Typ den Pointer casten zu müssen damit man damit arbeiten kann.

    Bei der Übergabe an Funktionen jedoch schon einmal (öfter). Wenn die Parameter dann an anderer Stelle als Typ X deklariert werden und somit feststeht wie die Daten im Speicher angeordnet sind.


  • Mod

    theSplit schrieb:

    Wutz schrieb:

    Und die hier noch gar nicht angesprochenen Alignment-Probleme bei Zeigercasts sind ein weiterer Grund, auf solche "universellen" Schnittstellen per void* mit Zeigercasts zu verzichten.

    Ich stelle mich gerne doof, was für "Alignment"-Probleme? Ein Pointer ist doch nur 32 bit oder (64 bit) "groß" (je nach Architektur) wenn dieser an eine Funktion übergeben wird?

    Du hast Alignment-Problematiken völlig verpeilt. Es geht um die Ausrichtung der Dinge, auf die ein Zeiger zeigt. Die Zeiger selbst sind als automatische Variablen selbstverständlich richtig ausgerichtet.



  • SeppJ schrieb:

    theSplit schrieb:

    Ich stelle mich gerne doof, was für "Alignment"-Probleme? Ein Pointer ist doch nur 32 bit oder (64 bit) "groß" (je nach Architektur) wenn dieser an eine Funktion übergeben wird?

    Du hast Alignment-Problematiken völlig verpeilt. Es geht um die Ausrichtung der Dinge, auf die ein Zeiger zeigt. Die Zeiger selbst sind als automatische Variablen selbstverständlich richtig ausgerichtet.

    Was ist daran problematisch? - Ein Void Pointer/Zeiger weiß nicht auf was für Werte er zeigt, bis man explizit auf einen Typ castet um dann so den Wert bzw. die Werte anzusprechen damit der Pointer weiß wie viel Byte ein Wert zum nächsten, im Speicher, hat. Oder im Falle von Structs, an welcher Stelle ein Wert abgelegt ist?

    Wenn ich ein realloc mache muß ich explizit casten, je nach Typ des Zeigers den ich zurückbekommen möchte.

    Wo soll das mit dem Void Pointern sonst problematisch sein? In Structs?

    Ich frage deßhalb so "plump" weil mir das keine Uni beibringt 😉



  • theSplit schrieb:

    Ich frage deßhalb so "plump" weil mir das keine Uni beibringt 😉

    Unausgerichtete Speicherzugriffe sind problematisch, aber das kann von Architektur zu Architektur - und auch von Anweisungsset zu Anweisungsset - variieren, WAS GENAU denn jetzt problematisch ist.

    Auf x86/x64 sind diese Zugriffe einfach nur langsamer als etwas, was auf 4/8-Bytes ausgerichtet ist. Wenn du jetzt allerdings SSE/AVX auf nicht ausgerichteten Speicher anwendest (SSE: 16 Bytes, AVX: 32 Bytes), bekommst du einen ... Segfault? Bin mir grad nicht sicher, aber Bus-Error gibt es meines Wissens nicht als Hardware-Fehler, obwohl es ja das korrekte Signal wäre.

    Ich habe aber auch mal von einem Fall gelesen, in dem ein Spiel auf einer bestimmten Architektur richtig schnell war, und auf dem Nachfolgermodell richtig lahm, weil unausgerichtete Zugriffe einen Fault verursacht haben, der dann vom Kernel behandelt wurde - und das richtig doll langsam. Das Spiel hat funktioniert, war aber nicht genießbar.



  • Ich sollte der Frage etwas mehr Zeit geben, aber:

    dachschaden schrieb:

    theSplit schrieb:

    Ich frage deßhalb so "plump" weil mir das keine Uni beibringt 😉

    [...]

    Auf x86/x64 sind diese Zugriffe einfach nur langsamer als etwas, was auf 4/8-Bytes ausgerichtet ist. [...]

    Der Compiler sorgt doch dafür dass Einheiten entsprechend angeordnet werden, in einem Struct.
    Das heißt das Integer Werte 4 Byte benötigen, Bool und Char 1 Byte, Short 4?

    Und das man das ganze schon mit dem Prozessor Direktive
    "#pragma pack(2)" / 4 / 6 / 8... steuern kann.

    GCC bietet zu dem die Möglichkeit, das Packing einzustellen, die genauen Werte habe ich nicht mit Kopf leider.

    Aber normalerweise werden Typen im Speicher so abgelegt das ein 4 Byte Symbol nicht auf einer Adresse 1 liegt, sondern auf 4 oder 2 (so fern möglich).

    So hab ich es bisher verstanden. Und mit Packing hatte ich in einem Anwendungsfall, bei dem aber relativ viel über dein Speicher ging (hier vorgestelltes Projekt) wenig Probleme bzw. auch keine Performanceeinbusen unter 64bit mit Intel CPU, GCC, Pragma Anweisung und auch Structure Packing.


  • Mod

    Einen Zeiger nach void* und wieder zurück zu seinem Ursprungstyp zu casten ist unproblematisch und es wird sogar ausdrücklich vom Standard verlangt, dass dies immer funktionieren muss. Nicht ohne Grund sind Umwandlungen dieser Art in C sogar implizit möglich. Problematisch werden Umwandlungen zwischen verschiedenen Typen (egal ob direkt oder über void*-Zwischenstationen): Woher weißt du mit Sicherheit, dass alles richtig ausgerichtet ist, wenn du einen Zeiger in einen anderen Zeigertypen mit anderen Alignmentanforderungen castest? Es ist nicht einmal so einfach, dass man sagen kann, dass gleiche Größe ausreicht; beispielsweise können Fließkommazahlen und Integerwerte trotz gleicher Größe unterschiedliche Anforderungen haben (z.B. für die Verarbeitung mit SSE).

    Prinzipiell ist es auch ausdrücklich undefiniertes Verhalten, zwei verschiedene Typen von Zeigern ineinander zu casten. Wenn der Compiler das sieht, kann er auch anfangen, irgendwelche recht unerwartete Optimierungen durchzuführen, bei denen das Ergebnis eines solchen Casts als undefiniert angenommen wird.



  • #pragma pack und andere Nicht-Standard-Compilererweiterungen betreffen Bytepadding innerhalb von struct, ich rede vom Alignment von Zeigern.
    Der C-Sprachstandard kennt auch kein 32/64/ Bit o.ä.

    Der C-Sprachstandard definiert für das Zeigeralignment:
    - für Funktionszeiger überhaupt nichts
    - alle struct-Zeiger sind untereinander safe
    - alle union-Zeiger sind untereinander safe
    - char/signed char/unsigned char/void Zeiger sind untereinander safe und haben immer das global "bestmögliche" Alignment
    Weiter wird nichts geregelt.
    D.h. alles andere ist UB.

    long s[] = {1,2,3,4};
    
    f(s);       /* der Compiler meckert? */
    f((int*)s); /* Cast drauf und gut is */
    
    void f(int *i)
    {
      printf("%d",i[0]); /* und spätestens hier verlässt man sich dann auf die Gnade des Compilerbauers */
    }
    

Anmelden zum Antworten