Problem mit Variablen vom Typ Double

Anfängerfragen zum Programmieren mit PureBasic.
Benutzeravatar
Sicro
Beiträge: 955
Registriert: 11.08.2005 19:08
Kontaktdaten:

Re: Problem mit Variablen vom Typ Double

Beitrag von Sicro »

NicTheQuick hat geschrieben:

Code: Alles auswählen

Global.i mem1, mem2, mem3

Procedure init()
   mem1 = AllocateMemory(1000)
   If mem1
      mem2 = AllocateMemory(1000)
      If mem2
         mem3 = AllocateMemory(1000)
         If mem3
         
            ;alles okay, weiter geht's
         Else
            FreeMemory(mem2)
            FreeMemory(mem1) ;Redundanz -> Schlecht!
            Debug "Fehlgeschlagen"
            ProcedureReturn #False
         EndIf
      Else
         FreeMemory(mem1)
         Debug "Fehlgeschlagen"
         ProcedureReturn #False
      EndIf
   Else
      Debug "Fehlgeschlagen"
      ProcedureReturn #False
   EndIf
   
   ProcedureReturn #True
EndProcedure
Früher habe ich es auch so gemacht, aber inzwischen verzichte ich auf Verschachtelungen, wenn möglich. So finde ich es viel übersichtlicher:

Code: Alles auswählen

Global.i mem1, mem2, mem3

Procedure init()

  mem1 = AllocateMemory(1000)
  If mem1 = 0
    Debug "Fehlgeschlagen"
    ProcedureReturn #False
  EndIf
  
  mem2 = AllocateMemory(1000)
  If mem2 = 0
    FreeMemory(mem1)
    Debug "Fehlgeschlagen"
    ProcedureReturn #False
  EndIf
  
  mem3 = AllocateMemory(1000)
  If mem3 = 0
    FreeMemory(mem2)
    FreeMemory(mem1)
    Debug "Fehlgeschlagen"
    ProcedureReturn #False
  EndIf
  
  ;alles okay, weiter geht's
  
  ProcedureReturn #True

EndProcedure
Mit Goto ist es meiner Meinung nach noch besser:

Code: Alles auswählen

Procedure init()
  
  mem1 = AllocateMemory(1000)
  If  mem1 = 0
    Debug "Fehlgeschlagen: mem1"
    Goto CleanUp
  EndIf
   
   mem2 = AllocateMemory(1000)
   If mem2 = 0
     Debug "Fehlgeschlagen: mem2"
     Goto CleanUp
   EndIf
   
   mem3 = AllocateMemory(1000)
   If  mem3 = 0
     Debug "Fehlgeschlagen: mem3"
     Goto CleanUp
   EndIf
   
   ProcedureReturn #True
   
   CleanUp:
   If mem1 : FreeMemory(mem1) : EndIf
   If mem2 : FreeMemory(mem2) : EndIf
   ProcedureReturn #False
   
EndProcedure
Bild
Warum OpenSource eine Lizenz haben sollte :: PB-CodeArchiv-Rebirth :: Pleasant-Dark (Syntax-Farbschema) :: RegEx-Engine (kompiliert RegExes zu NFA/DFA)
Manjaro Xfce x64 (Hauptsystem) :: Windows 10 Home (VirtualBox) :: Neueste PureBasic-Version
GPI
Beiträge: 1511
Registriert: 29.08.2004 13:18
Kontaktdaten:

Re: Problem mit Variablen vom Typ Double

Beitrag von GPI »

Ich würde das ganze so schreiben:

Code: Alles auswählen

Global.i mem1, mem2, mem3

Procedure init()
  Protected ret=#True  
  
  
   mem1 = AllocateMemory(1000)
   If Not mem1 : ret=#False : EndIf
   
   mem2 = AllocateMemory(1000)
   If Not mem2 : ret=#False : EndIf
   
   mem3 = AllocateMemory(1000)
   If Not mem3 : ret=#False : EndIf
   
   
   If ret
     ;more init-stuff
     ProcedureReturn #True
     
   Else
     If mem3
       FreeMemory(mem2)
       mem2=0
     EndIf
     If mem2
       FreeMemory(mem2)
       mem2=0
     EndIf
     If mem1
       FreeMemory(mem1)
       mem1=0
     EndIf
   EndIf
   
   ProcedureReturn #False
EndProcedure
Wobei ich streng genommen, ein Speicherblock mit der Gesamtgröße anfordern würde und dann verteilen :)
Ich würde auf jeden Fall den Pointer zum Speicherblock nach den freigeben auf null setzen, besonders wenn er global ist. Das kann in Zweifelsfall eine ewig lange Fehlersuche abkürzen.

Wobei in diesen Fall sogar sowas möglich wäre:

Code: Alles auswählen

Global.i mem1, mem2, mem3

 Procedure init_CleanUp()
   If mem3
     FreeMemory(mem2)
     mem2=0
   EndIf
   If mem2
     FreeMemory(mem2)
     mem2=0
   EndIf
   If mem1
     FreeMemory(mem1)
     mem1=0
   EndIf
 EndIf
 
EndProcedure

Procedure init()
  mem1 = AllocateMemory(1000)
  If Not mem1 :init_CleanUp():ProcedureReturn #False: EndIf
  
  mem2 = AllocateMemory(1000)
  If Not mem2 : init_CleanUp():ProcedureReturn #False: EndIf
  
  mem3 = AllocateMemory(1000)
  If Not mem3 : init_CleanUp():ProcedureReturn #False : EndIf
    
  ;more init-stuff
  ProcedureReturn #True
EndProcedure
Pauschale Antworten gibt es nicht :) Wie gesagt, ich sag nicht, das Goto unnütz ist, aber in der Regel gibt es andere Lösungen, die man bevorzugen sollte. Ich hab eigentlich seit jahrzehnten kein Goto mehr verwendet. Ich weich auch gerne auf eine Kontrollvariable aus, die ich auf #True/#False setze. Dabei kann man halt auch logische Blöcke zusammenfassen, so etwa:

Code: Alles auswählen

Procedure init()
  Protected ret=#True
  
  ;Initialize Block 1
  mem1=AllocateMemory(1000)
  If mem1=0
    ret=#False
  Else
    ;dosomething
  EndIf
  
  ;Initalize Block 2
  If ret
    mem2=AllocateMemory(1000)
    If mem2=0
      ret=#False
    Else
      ;dosomething
    EndIf
  EndIf
  
  ;Initalize Block 3
  If ret
    mem3=AllocateMemory(1000)
    If mem3=0
      ret=#False
    Else
      ;dosomething
    EndIf
  EndIf
  
  ;In Fehlerfall alles freigeben
  If ret=#False
    If mem3
      FreeMemory(mem2)
      mem2=0
    EndIf
    If mem2
      FreeMemory(mem2)
      mem2=0
    EndIf
    If mem1
      FreeMemory(mem1)
      mem1=0
    EndIf
  EndIf  
  
  ProcedureReturn ret
EndProcedure
CodeArchiv Rebirth: Deutsches Forum Github Hilfe ist immer gern gesehen!
Benutzeravatar
mhs
Beiträge: 224
Registriert: 11.01.2009 16:30
Wohnort: Graben
Kontaktdaten:

Re: Problem mit Variablen vom Typ Double

Beitrag von mhs »

Oje oje, immer dieses goto :lol: das spaltet die Gemüter :mrgreen:

Ich finde auch, dass man es so gut wie möglich nicht verwenden sollte. Und wenn überhaupt dann nur wie hier gezeigt für ein "aufräumen" bzw. sauberes verlassen einer Prozedur, wenn dadurch den Code übersichtlicher wird bzw. Redundanzen vermieden werden können.

Das Paradebeispiel wäre, wenn das AllocateMemory am Ende der Prozedur in jedem Fall freigegeben werden muss, egal ob Fehler oder nicht. Da wäre ein goto für mich vollkommen in Ordnung (Übersichtlicher, Kürzer und doppeltes FreeMemory vermieden). Ab wie so vieles ist das eine Sache des Geschmacks ein richtig oder falsch gibt es nicht :wink:

Code: Alles auswählen

Procedure init()
 
  ret = #False

  mem1 = AllocateMemory(1000)
  If mem1 = 0
    Debug "Fehlgeschlagen: mem1"
    Goto CleanUp
  EndIf
   
   mem2 = AllocateMemory(1000)
   If mem2 = 0
     Debug "Fehlgeschlagen: mem2"
     Goto CleanUp
   EndIf
   
   mem3 = AllocateMemory(1000)
   If  mem3 = 0
     Debug "Fehlgeschlagen: mem3"
     Goto CleanUp
   EndIf
   
   ; ... tu etwas ...
   ret = #True

   CleanUp:
   If mem1 : FreeMemory(mem1) : EndIf
   If mem2 : FreeMemory(mem2) : EndIf
   If mem3 : FreeMemory(mem3) : EndIf
   
   ProcedureReturn ret
   
EndProcedure
Michael Hack

Michael Hack Software :: Softwareentwicklung | Webentwicklung | IT-Dienstleistungen
www.michaelhacksoftware.de :: www.mh-s.de :: www.michael-hack.de
Benutzeravatar
mk-soft
Beiträge: 3695
Registriert: 24.11.2004 13:12
Wohnort: Germany

Re: Problem mit Variablen vom Typ Double

Beitrag von mk-soft »

Geht auch sehr gut ohne Goto mit Repeat : Break : Until

Code: Alles auswählen

Procedure MeineProc()
   Protected.i iFehler   
   
   Repeat
     If Bedingung1 = #False : iFehler = -1 : Break : EndIf
     If Bedingung2 = #False : iFehler = -2 : Break : EndIf
     If Bedingung3 = #False : iFehler = -3 : Break : EndIf
     
     ;Mache das was gewünscht ist
     ProcedureReturn #True
   Until #True
   ; Fehler
   ProcedureReturn iFehler
   
 EndProcedure
 
 Global NewList Values.i()
 
 Macro NewElement(MyList, Value)
   AddElement(MyList) : MyList = Value
 EndMacro
 
 NewElement(Values(), 100)
 NewElement(Values(), 200)
 NewElement(Values(), 300)
 
 Procedure MeineSuche(List MyList(), Value.i)
   
   Repeat
     ForEach MyList()
       If MyList() = Value
         Break 2
       EndIf
     Next
     ProcedureReturn 0
   Until #True
   ProcedureReturn MyList()
 EndProcedure
 
 Debug MeineSuche(Values(), 200)
 Debug MeineSuche(Values(), 500)
 
Alles ist möglich, fragt sich nur wie...
Projekte ThreadToGUI / EventDesigner V3 / OOP-BaseClass-Modul
Downloads auf MyWebspace / OneDrive
Benutzeravatar
NicTheQuick
Ein Admin
Beiträge: 8675
Registriert: 29.08.2004 20:20
Computerausstattung: Ryzen 7 5800X, 32 GB DDR4-3200
Ubuntu 22.04.3 LTS
GeForce RTX 3080 Ti
Wohnort: Saarbrücken
Kontaktdaten:

Re: Problem mit Variablen vom Typ Double

Beitrag von NicTheQuick »

Das AllocateMemory() war nur ein einfaches Beispiel. Natürlich könnte man
einfach 3000 Bytes allozieren, aber darum ging es nicht. Es ging ums
Aufräumen danach und das man nichts doppelt machen muss. Ein
FreeMemory() mag noch einfach sein und kann man auch mal doppelt
schreiben, aber es kann komplexere Aufräumarbeiten gehen und Fehler
schleichen sich nun mal ein. Vor allem, wenn man eine Stelle ändert und die
andere redundante Stelle vergisst.

Die Idee mit dem "If mem : FreeMemory(mem) : EndIf" ist wahrscheinlich
die beste und nutze ich auch häufig. Und diese lässt sich auch genauso gut
mit den IsXXX-Funktionen von Purebasic auf Gadgets, Dateien, Fenster,
Kryptographie, usw. anwenden.

Also vielleicht muss man nicht, wie ich im ersten Beispiel, mehrere
Sprungmarken erstellen, sondern kann einfach das "If mem" verwenden.
Legt man auf weniger Instruktionen und weniger komplexe Branchprediction
Wert, dann sind mehrere Sprungmarken sinnvoll, da keine Bedingungen
geprüft werden müssen.

@mk-soft:
"Repeat : Break : Until" finde ich persönlich überhaupt nicht schön. Das kann
man persönlich für seine eigenen Codes ja gerne nutzen, aber wird der Code
mal veröffentlicht und andere schauen ihn sich an, sehen sie eine Schleife
und gehen natürlich davon aus, dass hier etwas geloopt wird. Gerade wenn
die Schleife mehr als Bildschirmfüllend ist, verwirrt das doch erst mal
unnötig. Dann wirklich lieber ein Goto.

@mhs:
Wenn der Speicher am Ende der Procedure sowieso immer freigegeben
werden soll, dann klappt das super und ohne Redundanz mit verschachtelten
Ifs oder eben auf deine Art und Weise. Das finde ich beides okay. Ich hab in
meinem Beispiel meine Procedure aber extra init() genannt, weil sie etwas
initialisieren soll, was nach ihr noch bestehen soll. Und darum ging es mir.
Das macht das Aufräumproblem eben etwas interessanter.

@GPI:
Das Nullsetzen von mem* ist natürlich eine gute Idee und verhindert Fehler.
:D Auf jeden Fall ein guter Tipp für alle Pointerer hier. Eine extra Funktion
init_CleanUp() finde ich zwar etwas überrissen, aber klar, funktioniert hier
auch. Dennoch tendiere ich dann eher zu Goto. Dann kann ich mir auch das
x-fache "ProcedureReturn #False" sparen.

@Sicro:
Dein erste Code ohne Verschachtelungen und ohne Goto hat wieder
Code-Dopplungen, nämlich FreeMemory(mem1). Wie schon oben erwähnt,
bin ich da kein Fan von. Aber mit deinem zweiten Code sind wir uns einig. :)
Bild
Benutzeravatar
Sicro
Beiträge: 955
Registriert: 11.08.2005 19:08
Kontaktdaten:

Re: Problem mit Variablen vom Typ Double

Beitrag von Sicro »

GPI hat geschrieben:Ich würde auf jeden Fall den Pointer zum Speicherblock nach den freigeben auf null setzen, besonders wenn er global ist.
Jo, sehr guter Hinweis. Das mache ich auch so.
NicTheQuick hat geschrieben:@Sicro:
Dein erste Code ohne Verschachtelungen und ohne Goto hat wieder
Code-Dopplungen, nämlich FreeMemory(mem1). Wie schon oben erwähnt,
bin ich da kein Fan von.
Jo, ist ja nur eine Entschachtelung deines Codes. Bei deinem ist ja FreeMemory(mem1) auch doppelt.

Ansonsten sehe ich das so wie NicTheQuick.
Bild
Warum OpenSource eine Lizenz haben sollte :: PB-CodeArchiv-Rebirth :: Pleasant-Dark (Syntax-Farbschema) :: RegEx-Engine (kompiliert RegExes zu NFA/DFA)
Manjaro Xfce x64 (Hauptsystem) :: Windows 10 Home (VirtualBox) :: Neueste PureBasic-Version
Antworten