StructureUnion

Für allgemeine Fragen zur Programmierung mit PureBasic.
Toshy
Beiträge: 713
Registriert: 22.03.2005 00:29
Computerausstattung: Computer und Strom vorhanden
Wohnort: LK Wolfenbüttel

StructureUnion

Beitrag von Toshy »

StructureUnion sind nur nützlich für fortgeschrittene Programmierer, welche etwas Speicher sparen wollen, indem sie einige Felder innerhalb derselben Struktur teilen.
Wie spart StructureUnion Speicher bzw. wie funktioniert es?
Ein Struktur ist ja ein normal ein zusammenhängender Speicherbereich auf den nur einfacher zugegriffen werden kann.

Code: Alles auswählen

structure test
var1.l
var2.b
var3.w
endstructure
eine variable mit dieser Struktur würde 7 Bytes an Datenspeicher verbrauchen (Speicher für die Zuweisung der Variablen wie auch immer das funktioniert mal weg gelassen).
Dabei sind diese 7 Bytes hintereinander zusammenhängend.
hänge ich nun als vierte Variable einen String an

Code: Alles auswählen

structure test
var1.l
var2.b
var3.w
var4.s
endstructure
so erweitert sich der "Datenspeicherverbrauch" um weitere 4 Byte (8 bei 64 bit) direkt innerhalb der Struktur. unter "var4" steht dann der Pointer zum eigendlichen String (der dann natürlich auch Speicher verwendet).
Soweit so gut.

Jetzt verwende ich als Beispiel

Code: Alles auswählen

structure test
var1.l
var2.b
StructureUnion
      varA.b
      varB.b
      varC.b
EndStructureUnion
var3.w
var4.s
endstructure
Das VarA, VarB und VarC den selben Speicherbereich nutzen habe ich verstanden, damit betrifft jede Änderung dieser Daten auch ALLE "Variablen" welche diese Struktur nutzen. Allerdings fragt sich jetzt wo die Daten der Struktur "test" an sich im Speicher stehen. Hintereinander können sie ja nicht stehen.
Ich würde jetzt davon ausgehen, da Var1 bis Var4 zwar weiterhin "normal" hintereinander im Speicher zu finden sind. Und für alle Teile von varA bis varC ein einziger Pointer verwendet wird, der auf den Bereich zeigt, wo die weiteren geteilten Daten stehen.
Nun stellen sich folgende Fragen
1. Ist dem so
2. Steht der Pointer im Speicherbereich zwischen var2 und var3 oder wird er irgendwie "intern" außerhalb dieser Daten gespeichert?
3. Wie viel Speicher verbraucht dieses StructurUnion je strukturierter Variablen?

Denn alleine ein Pointer verbraucht ja vier Bytes bzw. 8 unter 64 bit Systemen. Da wäre würde man in meinem Beispiel durch den Pointer mehr speicher verbrauchen, als die drei Bytes zusammen von varA bis varC. Von Vorteil kann das natürlich trotzdem sein, wenn man einfach Daten für viele Strukturvariablen auf einmal ändern will.
Viel Speicher wird man sicher sparen können, wenn man Strings nutzt oder viele Variablen innerhalb einer "StructurUnion" verwendet. Aber auch nur, wenn nicht je Variable ein Pointer genutzt wird. Dies glaube ich aber nicht, denn das würde der optimierten "programmierung von Purebasic" wiedersprechen.

Es wäre aber nett, wenn mich da einer aufklärt.

Danke schön.

Gruß
Toshy
1. Win10
PB6.1
Derren
Beiträge: 558
Registriert: 23.07.2011 02:08

Re: StructureUnion

Beitrag von Derren »

Das Beispiel in der Hilfe macht es doch eigtl. recht deutlich.
Es wird nichts "gleichzeitig" gespeichert, sondern das Feld wird immer mit dem letzten Wert überschrieben.

Code: Alles auswählen

 Structure Type
    Name$
    StructureUnion
      Long.l      ; Jedes Feld (Long, Float und Byte) befindet sich
      Float.f     ; an derselben Adresse im Speicher.
      Byte.b      ;
    EndStructureUnion
  EndStructure
  
  
  Define var.Type
  
  var\Long = 9999
  var\Byte = 19
  
  Debug var\Long
  Debug var\Byte
Hier wird das mit dem "Überschreiben" noch deutlicher. Gibt nämlich einen IMA, also aufpassen....

Code: Alles auswählen

 Structure Type
    StructureUnion
      int.i
      *str
    EndStructureUnion
  EndStructure  
  
  Define var.Type
  
  var\int = 1
  var\str = @"Hallo Welt"
  
  Debug var\int
  Debug PeekS(var\str)
  

  var\str = @"Bye Bye, Welt"
  var\int = 17
  
  Debug var\int
  Debug PeekS(var\str)


Man benutzt das eigtl für sowas (in komplexeren Systemen).
Beide Variablen foo und bar haben die gleiche Struktur. Die strukturierte Variable ist aber nur so groß, als wenn man zwei einzelne Strukturen verwendet hätte.
Der obere Code ist also im Prinzip das gleiche, wie der auskommentierte Code darunter

Code: Alles auswählen

 Structure Type
    StructureUnion
      int.i
      float.f
    EndStructureUnion
  EndStructure
  
  
  Define foo.Type 
  Define bar.Type 
  
  
  foo\int = 99
  bar\float = 0.5
  
  Define var.f = foo\int * bar\float
  Debug var
  
  
;   Structure TypeA
;   	int.i
;   EndStructure 
;   
;   Structure TypeB
;   	float.f
;   EndStructure 
;   
;    Define foo.TypeA
;   Define bar.TypeB 
;   
;   
;   foo\int = 99
;   bar\float = 0.5
;   
;   Define var.f = foo\int * bar\float
;   Debug var
Signatur und so
Toshy
Beiträge: 713
Registriert: 22.03.2005 00:29
Computerausstattung: Computer und Strom vorhanden
Wohnort: LK Wolfenbüttel

Re: StructureUnion

Beitrag von Toshy »

Moin Moin.
Leider geht deine Erklärung total an meinem Anliegen vorbei. Und die Antwort findet man leider auch nicht in der Hilfe.
Es geht mir nicht direkt darum wie man die StructureUnion nutzt, auch hat es nicht mit "gleichzeitig" zu tun (auch wenn man über Threads ja schon gleichzeitig darauf zugreifen kann).
Ich habe versucht mit der Erklärung und vor allen den Fragen heraus zu bekommen, wo die und wie die Daten gespeichert werden. Wo ihre "Bytepositionen" sind. Wie ich ganz zum Schluß erkläre, verstehe ich diese Art von Struktur auch noch nicht ganz (Speichertechnisch) und damit wie man sie nutzt. Auch wenn ich schon etwas vermute, mal noch etwas Code aus meinen "Gedankengängen" der Vergangenheit.

Code: Alles auswählen

Debug "ohne StructureUnion:"
Structure test2
  var1.b
  var2.b
    varA.b
    varB.b
    varC.b
  var3.b
  var4.b
EndStructure

poo2.test2

poo2\var1 = 1
poo2\var2 = 2
poo2\varA = 11
poo2\varB = 12
poo2\varC = 13
poo2\var3 = 3
poo2\var4 = 4

Debug "@poo2 = " + Str(@poo2)

For i = 0 To 6
  Debug "adresse = " + Str(@poo2+i) + " Inhalt = " + Str( PeekB(@poo2+i) )
Next i
Debug "sizeOf(poo2) = " + Str(SizeOf(poo2))

Debug "mit StructureUnion:"

Structure test
  var1.b
  var2.b
  StructureUnion
    varA.b
    varB.b
    varC.b
  EndStructureUnion
  var3.b
  var4.b
EndStructure

poo.test

poo\var1 = 1
poo\var2 = 2
poo\varA = 11
poo\varB = 12
poo\varC = 13
poo\var3 = 3
poo\var4 = 4

Debug "@poo = " + Str(@poo)

For i = 0 To 6
  Debug "adresse = " + Str(@poo+i) + " Inhalt = " + Str( PeekB(@poo+i) )
Next i

Debug "sizeOf(poo) = " + Str(SizeOf(poo))
Der Überschaubarkeit wegen, habe ich nur "Byte" als Variablentyp genommen.
Ergebnis bei mir:
[02:03:39] ohne StructureUnion:
[02:03:39] @poo2 = 4394684
[02:03:39] adresse = 4394684 Inhalt = 1
[02:03:39] adresse = 4394685 Inhalt = 2
[02:03:39] adresse = 4394686 Inhalt = 11
[02:03:39] adresse = 4394687 Inhalt = 12
[02:03:39] adresse = 4394688 Inhalt = 13
[02:03:39] adresse = 4394689 Inhalt = 3
[02:03:39] adresse = 4394690 Inhalt = 4
[02:03:39] sizeOf(poo2) = 7
[02:03:39] mit StructureUnion:
[02:03:39] @poo = 4394692
[02:03:39] adresse = 4394692 Inhalt = 1
[02:03:39] adresse = 4394693 Inhalt = 2
[02:03:39] adresse = 4394694 Inhalt = 13
[02:03:39] adresse = 4394695 Inhalt = 3
[02:03:39] adresse = 4394696 Inhalt = 4
[02:03:39] adresse = 4394697 Inhalt = 0
[02:03:39] adresse = 4394698 Inhalt = 0
[02:03:39] sizeOf(poo) = 5
Beide Testdurchläufe machen im Grunde das selbige, einzig die Positionen wo PB / das System (wie auch immer?) die Daten ablegt sind unterschiedlich. Ohne StructureUnion kann man mit Byteweisen auslesen leicht sehen, das die Daten hintereinander liegen.
Mit StructureUnion ist mir zwar vorher klar gewesen, das es ein anderes Ergebnis gibt, aber welches WUßte ich nicht. Nach dem Testen konnte ich mir die Ergebnisse aber einfach nicht sicher erklären.
Egal ob ich jetzt davon ausgehe, das es sich ab Adresse 4394694 (die Ergebnisse 13,3,4) um die von mir eingegebenen Werte handelt oder ob rein zufällig die selben Werte erscheinen, verstehe ich es nicht.
Falls die "13" nicht drinn wäre, hätte ich mir noch denken können, das einfach der "Nicht StructureUnion-Teil" hintereinander gespeichert wird und der andere Teil über einen Pointer der "wie und wo" auch immer außerhalt diesen Bereiches gespeichert wird.
Das Strukturesize nun 5 ergibt hätte ich gar nicht gedacht. 4 oder 7, das ja, aber so...

Da ich StructureUnion aber noch nie verwendet habe, ist mir auch klar, das ich vielleicht deren Funktion noch nicht richtig verstehe. Aber in der Hilfe steht ja auch quasi gar nichts dazu. Wer es nicht schon weiß, dem erschließt sich das nicht gleich. Denn mit einem einzigen Satz in der Hilfe ist einem nicht unbedingt weiter geholfen.

Vom Text her kann man da als Unwissender auch was ganz anderes rein interpretieren, als es wohl nach meinen Testversuchen ist.

"indem sie einige Felder innerhalb derselben Struktur teilen" und "Jedes Feld (Long, Float und Byte) befindet sich an der selben Adresse im Speicher" klang für mich selbst nach immer wieder lesen und auch jetzt wo ich was anderes denke eher danach, da egal welche "Variable" die "selbe Struktur" verwendet jeweils die "Felder aus der Union-Sektion" je Typ im selben Speicher stehen. Also global.

Aber nach dem testen sieht es halt so aus, also ob es eher so ist:
Innerhalb jeden eigenständigen strukturierten Speicherbereichs (wie z.B. einer strukturierten Variable) beginnen die Speicherbereiche aller Felder innerhalb der "StructureUnion" an der selben Speicheradresse. Dabei ist es unterheblich ob unterschiedliche Variablentypen genutzt werden. Sie alle werden an der selben Startadresse abgelegt. Diese Startadresse liegt innerhalb des strukturierten Speicherbereichs. Die Speichergrößte des StrutureUnions-Bereichs entspricht dem größten Feld innerhalb des Bereichs. StructureUnion bezieht sich jeweils nur auf den mit dieser Strukture allokierten Bereich. Unterschiedle Variablen oder Speicherbereiche mit selber Struktur nutzen nie den selben Speicherbereich.
Das ist jetzt wohl auch noch nicht perfekt ausgedrückt, liegt aus meiner Sicht dem was das Beispiel her gibt und ich ausgetest habe aber recht nahe. So scheint es aus meiner Sicht zu laufen, steht nur leider so nicht in der Hilfe.
Wenn es so ist, dann ist das an sich eine ganz simple Sache, auch wenn ich sowas gerade nicht gesucht habe.

Ich bin also eher über den Hilfetext irritiert.

Grüßle und gute Nacht
Toshy
1. Win10
PB6.1
Benutzeravatar
CSHW89
Beiträge: 489
Registriert: 14.12.2008 12:22

Re: StructureUnion

Beitrag von CSHW89 »

Hallo Toshy,

der Text, den du am Ende zitierst, trifft es eigentlich genau. SturctureUnion benötigt keinen zusätzlichen Speicher in Form von Pointern oder dergleichen. Ein StructureUnion-Block braucht genau so viel Platz, wie die größte Variable braucht. Vielleicht hier noch eine kleine Demostration:

Code: Alles auswählen

Structure test
  b.b
  w.w
  StructureUnion
    byte.b
    quad.q
    long.l
  EndStructureUnion
  q.q
EndStructure
Global test.test

Debug @test
Debug @test\b    ; @test + 0
Debug @test\w    ; @test + 1
Debug @test\byte ; @test + 3 (genau hinter w)
Debug @test\quad ; @test + 3 (an der gleichen Position)
Debug @test\long ; @test + 3 (ebenfalls)
Debug @test\q    ; @test + 11 (genau hinter quad, da quad größte Variable ist mit 8, also 11=3+8)
lg Kevin
Bild Bild Bild
http://www.jasik.de - Windows Hilfe Seite
padawan hat geschrieben:Ich liebe diese von hinten über die Brust ins Auge Lösungen
Benutzeravatar
NicTheQuick
Ein Admin
Beiträge: 8838
Registriert: 29.08.2004 20:20
Computerausstattung: Ryzen 7 5800X, 64 GB DDR4-3200
Ubuntu 24.04.2 LTS
GeForce RTX 3080 Ti
Wohnort: Saarbrücken

Re: StructureUnion

Beitrag von NicTheQuick »

Ist das wirklich so schwer zu verstehen? Ich habe das mal anhand eigener Beispiele extra für dich erklärt.

Code: Alles auswählen

Structure test
	StructureUnion
		l.l
		b.b[4]
	EndStructureUnion
EndStructure
Define t.test
; l.l und b.b[4] sind beide 4 Bytes groß und teilen sich den selben Speicher, d.h. der Pointer
; zu l.l ist der selbe der auf b.b[0] zeigt:

Debug "@t\l = @t\b[0]"
Debug Str(@t\l) + " = " + Str(@t\b[0])

; Schreibt man jetzt in l.l den Wert 1, erscheint dieser auch in b[0]
t\l = 1
Debug "t\b[0] = " + Str(t\b[0])
; Schreibt man andersrum in b[0] den Wert 2, erscheint er auch in l:
t\b[0] = 2
Debug "t\l = " + Str(t\l)

; Was passiert, wenn wir l wieder 0 setzen und in b[1] = 1 schreiben?
t\l = 0
t\b[1] = 1
Debug "t\l = " + Str(t\l)

; Das wird klar, wenn wir das in hexadezimaler Schreibweise ausdrücken.
; Also schreiben wir mal $12345678 in t\l und lassen uns alle t\b[i] als Hex ausgeben
t\l = $12345678
Define i.i
For i = 0 To 3
	Debug "t\b[" + Str(i) + "] = $" + Hex(t\b[i])
Next

; Nanu? Die Teilwerte $12, $34, $56 und $78 stehen rückwärts in t\b[i] drin.
; Das liegt daran, dass ein zweistelliger Hexwert genau ein Byte beansprucht, 
; wir in t\l somit eine "vierbyte-ige" Zahl geschrieben hab und da t\b[i] sich
; den Speicher mit t\l teilt, können wir mit t\b[i] auf jedes einzelne Byte von
; t\l zugreifen.

; Das hat nichts mit Threads, gleichzeitigen Zurgriffen oder sonstwas zu tun. Alle
; Strukturelement innerhalb einer StructureUnion haben die selbe Startadresse, teilen
; also zwangsweise den selben Speicher. Dabei gibt das größe Element die größe der
; Union an. Das sieht man im nächsten Beispiel:
Debug ""
Debug "Zweites Beispiel:"
Structure test2
	StructureUnion
		b.b			;SizeOf(Byte) = 1
		q.q			;SizeOf(Quad) = 8
	EndStructureUnion
EndStructure

; Wie groß müsste also die Struktur test2 sein? Rchtig! 8 Bytes:
Debug "SizeOf(test2) = " + Str(SizeOf(test2))

; Und jetzt noch eine Abwandlung deiner Testfälle:
Debug ""
Debug "Drittes Beispiel:"
Structure test3
	StructureUnion
		varA.b
		varB.b
		varC.b
		varD.b
		varE.b
	EndStructureUnion
EndStructure
Define t3.test3
; Alle Variablen in der Union hab die Größe 1, also ist auch die Größe der Union
; selbst 1. Und da varA bis varE sich den selben Speicher teilen, enthalten sie
; natürlich auch immer den selben Wert. Folgendes ist also möglich:
t3\varA = 1
t3\varD = t3\varB + t3\varC
Debug "t3\varE = " + Str(t3\varE)
; Verwirrt? Eigentlich ganz einfach:
; varA wird 1 gesetzt, damit erhalten varB bis varE den selben Wert. Rechnen
; wir also varD = varB + varC ist das aufgelöst varD = 1 + 1 = 2. varD ist nun
; also gleich 2, aber wiederum sind auch varA, varB, varC und varE ebenfalls 2.
Wenn du es jetzt noch nicht verstanden hast, muss es wohl ein anderer versuchen. Dann geb' ich auf.
Toshy
Beiträge: 713
Registriert: 22.03.2005 00:29
Computerausstattung: Computer und Strom vorhanden
Wohnort: LK Wolfenbüttel

Re: StructureUnion

Beitrag von Toshy »

Danke CSHW89.
Dann ist ja gut.

Auch an die anderen Danke.
Die wirkungsweise selbst habe ich dann ja durch probiern sehr wohl verstanden, aber der (wohl eher nicht vorhandene) Hilfetext ist halt nicht richtig oder deutlich. Selbst den Code in der Hilfe habe ich ja anfangs wohl (auch durch das testen diesen Codes) verstanden, nur konnte ich mir das Ergebnis nicht ganz erklären, da der Text halt irgendwie anders klang.

Aus der Sicht von jemand der es weiß klingt das halt anders als für Jemanden, der zum Einstieg in diesen Befehl nur den Text liest.

CSHW89 erste drei Sätze haben ja alles aufgeklärt. Ich werde mir mal überlegen und im entsprechenden Forenthema zur Hilfe schreiben, wie das vielleicht besser zu umschreiben ist.
(das Beispiel selbst ist ja gut)

Und Nick, danke für dein Mühe.
Ich hab es ja jetzt schon vorher an sich kapiert, nur nicht passend zum Text.
(l.l ist zwar 4 Byte groß, b.b aber nicht, b.b. ist ein Byte groß).
Und ging es mir ja schon zu Anfang NICHT darum (auch schon mehrfach wiederholt), das l.l und b.b am selben Bereich beginnen, sondern darum WO dieser bereich zu finden ist. Innerhalb der Struktur oder außerhalb(wie halt ein "dynamischer" String in dem Strukturspeicher nur den Pointer beinhaltet, nicht die Daten). und halt wer sich den Speicher "teilt". Jetzt ist das ja geklärt.

Danke
Toshy
1. Win10
PB6.1
Antworten