Gamestudio Links
Zorro Links
Newest Posts
Data from CSV not parsed correctly
by dr_panther. 05/06/24 18:50
Help with plotting multiple ZigZag
by degenerate_762. 04/30/24 23:23
M1 Oversampling
by 11honza11. 04/30/24 08:16
AUM Magazine
Latest Screens
The Bible Game
A psychological thriller game
SHADOW (2014)
DEAD TASTE
Who's Online Now
3 registered members (AndrewAMD, TipmyPip, 7th_zorro), 877 guests, and 4 spiders.
Key: Admin, Global Mod, Mod
Newest Members
firatv, wandaluciaia, Mega_Rod, EternallyCurious, howardR
19050 Registered Users
Previous Thread
Next Thread
Print Thread
Rate Thread
Page 2 of 3 1 2 3
Re: [solved] struct-Name per Laufzeit erstellen [Re: JoGa] #383237
09/17/11 17:37
09/17/11 17:37
Joined: Jul 2008
Posts: 894
T
TechMuc Offline
User
TechMuc  Offline
User
T

Joined: Jul 2008
Posts: 894
Ich hole im weiteren Post sehr weit aus - wenn du verschiedene Sachen schon weißt - sorry for that.

Quote:
Mein Gehirnwindungsproblem ist jetzt, dass ich 5 "virtuelle" Objekte habe, die aber alle den selben Pointer "Tomate" haben. Und das beisst sich bei mir im Kopf mit dem pointer "player", der ja nur auf ein Entity gesetzt werden kann. Soweit ich das jetzt verstanden habe, sind das ja aber 2 Paar Stiefel (Struct-Pointer und Entity-Pointer).
Doch per structs lassen sich doch neue Engine-objekte (wie z.B Strings) erstellen - und 2 gleiche stringnamen sind ja auch nicht möglich - warum also bei denn Ojekten "Tomate"?


Denke nicht so realistisch. Denke in Bytes.. ist einfacher wink
Erstmal: Was macht der Befehl malloc? Dazu mal ein stark vereinfachtes Beispiel:
Code:
for(int i=0;i<5;i++) {
  Object* obj = malloc(sizeof(Object));
  //more code
}


Sagen wir mal wir leben vor 20 Jahren wo du in deinem Prozess nur 256 Bytes an RAM hattest - Ist leichter darzustellen. Am Anfang ist der ganze RAM frei (vor der ersten malloc anweisung). Nun führst du den Befehl "malloc(sizeof(OBJECT));" aus, was ja für den PC nichts anderes ist als z.B. malloc(80);. Jetzt wird der PC den Speicher durchsuchen, wo noch 80 Bytes frei sind. Findet er, da noch alles direkt frei ist natürlich an erster Stelle. Sagen wir dein Prozess hat Speicherzugriff ab der RAM Position 0x1, dann wird die malloc Anweisung 0x1 zurück liefern.

Nach der erstmaligen Ausführung des Befehls:
Object* obj = malloc(sizeof(Object));
Hat die Variable obj also den Wert 0x1, wobei obj auf einen Speicherbereich zeigt der 80 Byte groß ist (s. Behauptung oben, dass sizeof(object) = 80).

Nun wird die Schleife durchlaufen, und du kommst wieder zu den malloc Befehl. Der PC durchsucht den RAM wieder und erkennt, dass an Stelle "0x1" ein Objekt angelegt ist mit der Größe 80. Daher kann er hier nichts ablegen und springt da rüber. Kommt raus an Stelle 0x51h (Hexadezimal.. Dezimal 81). Hier erkennt er, dass das frei ist. D.h. malloc registriert jetzt an dem PC: "Ab jetzt wird die Speicherstelle 0x51h - 0XA1h nicht mehr vergeben".
Malloc liefert also 0x51h zurück. Das heißt:

Nach der zweiten Ausführung des Befehls:
Object* obj = malloc(sizeof(Object));
hat obj jetzt den Wert 0x51h.

Das heißt: Für den PC ist das alte "Object" etwas völlig anderes als das neue "Object". Das eine ist was 80 Byte großes was an Position 0x1 liegt, das andere ist was 80 Byte großes was an Position 0x51h liegt. Mehr weiß der PC nicht und mehr will er auch nicht wissen laugh
Für dich heißt das: Wenn du mit den Objekten kommunizieren willst (also auf diese angelegten Objekte zugreifen willst), musst du dir genau diese Zahl merken (0x1h oder 0x51h).
Dies führt auch dazu, dass du dir irgendwie merken musst, dass du da an Stelle 0x1 und 0x51 ein Objekt angelegt hast - wenn du dir das nicht merkst sind diese Daten verloren und du hast ein Memory leak produziert (wie sollst du dem PC sagen, dass du die Daten nicht mehr brauchst - also wie gibst du die Daten per "free" frei - wenn du dir nicht gemerkt hast wo sie liegen?).

So weiter im Text. Wenn ich dich richtig verstehe meinst du so einen Code:
Code:
struct Object {
   ENTITY* ent;
   STRING* name;
};
Objekt* Tomate = malloc(sizeof(Object));
Tomate->ent = ent_create("..."....);
Tomate = malloc(sizeof(Object));
Tomate->ent = ent_create("...")...);
Tomate = malloc(sizeof(Object));
//..



Hier hast du jetzt 3x ein objekt Tomate angelegt. Ich mach mal mit meinen Byte Positionen von oben weiter.
Das erste Objekt wird an Position 0x1 erstellt. Nun greifst du auf Object->Ent zu. Was passiert jetzt intern? Der PC versucht das natürlich irgendwie zu verstehen und auf Byteebene runterzurechnen. Also der PC weiß, dass er "Tomate" an Stelle 0x1 liegt. Nun muss er nur noch wissen, was für einen Offset "Ent" innerhalb des Objektes "Tomate" hat, und welche Größe "Ent" hat. Der Offset ist, da "Ent" an erster Stelle liegt: 0. Die Größe von Ent ist 4 (da es ein Pointer ist) ==> Der PC weiß: Ich überschreibe jetzt Speicherbereich 0x1 - 0x5 mit dem Wert der von der Funktion ent_create zurückgeliefert wird.

Wenn du nun malloc nochmal Aufrufst und wieder im Objekt "Tomate" speicherst hast du die alte Speicherposition verloren. Du kommst da auch nie wieder hin, außer du hast wirklich nur 256 Byte RAM laugh
Das ist der Grund warum das Objekt Tomate speichern musst. Hier mal der Beispielscode einer verketten Liste für sowas:

Code:
struct object {
  ENTITY* ent;
  object* next;
  object* prev;
};

object* current_head = 0;
object* current_tail = 0;

int object_create(STRNG* str, VECTOR vpos, EVENT evt) {
  object* obj = malloc(sizeof(object));
  if(!current_head) {
    current_head = obj;
    current_tail = obj;
    obj->next = 0;
    obj->prev = 0;
  }
  else {
    current_tail->next = obj;
    obj->next = 0;
    obj->prev = current_tail;
    current_tail = obj;
  }
  obj->ent = ent_create(str,vpos,evt);
  return obj;
}

//BEIM LÖSCHEN MUSS DAS WIEDER AUS DER VERKETTEN LISTE RAUS!




Wenn du jetzt via object_create ein Objekt erschaffst, kann keiner der vielen Pointer mehr verschwinden, da sie alle in der verketten liste abgelegt sind.

Und nochmal ganz wichtig: Der Name eines Objektes interessiert den PC kein Stück. Ob du jetzt Object* tomate oder Object* blabber; schreibst. Der PC weiß nur, dass es ein 4 Byte großer Pointer ist der irgendwo im RAM liegt.

Quote:
Nehmen wir an, der Spieler klickt auf eine Tomate nach der anderen, um sie in sein Obstkörbchen zu packen.
Kann ich da so herangehen, dass ich ein Variable "Maus_gegenstands_ID" erstelle.
Wenn nun der Spieler auf eine Tomate klickt, sucht sie in ihrem Klick-Event in dieser verketteten Liste nach ihrem Struct und kopiert dann die ID-Nummer des Objektes (die beim Truhenöffnen den einzelnen Tomaten per Zufall zugewiesen wurde) in die Variable "Maus_gegenstands_ID".


ID würde ich nicht machen, da recht langsam. Generell würde ich empfehlen, einen Pointer auf die "Object" Struktur in einem Skill abzuspeichern.
Das ist vieeel schneller als das traversieren durch die verkette Liste.
Beispiel hierfür:

Code:
struct object {
  ENTITY* ent;
  //weitere daten
};

int object_create(STRNG* str, VECTOR vpos, EVENT evt) {
   object* obj = malloc(sizeof(Object*));
   obj->ent = ent_create(str,vpos,evt),
   obj->ent->skill[99] = (int)(void*)obj;
   return obj;
}

int object_delete(ENTITY* ent) {
   if(ent->skill[99]) {
     object* obj = (object*)ent->skill[99];
     free(obj);
   }
   ent_remove(ent),
   return 0;
}



Das ist die schnellste Lösung. Das Problem ist, dass du hier dafür veranwortlich bist, dass am Ende alle Entities von DIR gelöscht werden (via object_remove) und nicht mit ptr_remove/ent_remove oder von der engine bei einem neuen level_load.

Kurz zu erläuterung: Beim anlegen des Objektes wird das Objekt wieder an Speicherposition 0x1 erschaffen. Diese Speicherposition ist wie bei allen 32 bit Anwendungen exakt 4 Byte groß. Ein Skill ist auch exakt 4 Byte groß (so ist "var" definiert, und ein skill ist ja vom Typ var). Das heißt du kannst den Pointer zu dem Objekt, statt in einer verketten liste auch einfach in dem Skill abspeichern. Lass dich nicht dadurch irritieren, dass du einen Skill vll. nicht als Pointerspeicher kennst. Für den PC ist ein Skill exakt 4 Byte groß, der Pointer ist 4 Byte groß - passt - also kann der Pointer in den Skill rein. Fertig für den PC.
Den Compiler musst du noch "überzeugen", dass das wirklich geht, daher die Typecasting Anweisungen. Ohne Typecasten würde der Compiler (vermutlich) sagen: "Ne, (Object*) ist nicht das gleiche wie (var) - mach ich nicht mit". Mit dem Typecasten zwingst du dem Compiler zu akzeptieren, dass (Object*) eigentlich ja das gleiche wie ein (var) ist und speicherst das Objekt in dem Skill.

Dadurch ist der Pointer nicht verloren gegangen (auch wenn du ihn nicht in einer liste o.ä. abspeicherst) und du hast weiter Zugriff druff ==> Kein Memory Leak Produziert. Passt.


So kA.. so viel geschrieben und weiß nicht ob das alles sinnvoll ist. Naja stimmen sollte es (auch wenn die Deutsche Sprache vermutlich nicht so toll ist wink )

Last edited by TechMuc; 09/17/11 17:38.
Re: [solved] struct-Name per Laufzeit erstellen [Re: TechMuc] #383300
09/18/11 18:51
09/18/11 18:51
Joined: Dec 2003
Posts: 988
Germany, Magdeburg
JoGa Offline OP
User
JoGa  Offline OP
User

Joined: Dec 2003
Posts: 988
Germany, Magdeburg
Hey

vielen Dank für deine rießige Erklärung, sie hat mir sehr viel geholfen.
2 Fragen habe ich noch :-/
In deinem Beispiel hast du die Datenposition in einem Skill gespeichert:
Code:
obj->ent->skill[99] = (int)(void*)obj;


und später wieder ausgelesen:
Code:
object* obj = (object*)ent->skill[99];


1. Frage: Warum benötigt man das "void"? Das int erscheint mir nach deiner Erklärung logisch, doch das "void" kann ich mir nicht erklären (was es bedeutet [Funktionstyp ohne Rückgabewert] weis ich einiger maßen), doch verstehe ich die Bedeutung in deinem Kontext nicht.
2. Frage: Ich habe nach deinem Beispiel versucht, mir ein Gegenstand zu programmieren, den ich anklicken kann und dann eine gespeicherte bmp-Datei aus dem Struct heraus zu laden. Es erzeugt aber ein script-crash:SYS und ich bin mir nicht sicher, was ich bei der Zuweisung falsch gemacht habe:

Der Fehler liegt im Event. Es läuft alles rund, solang ich die Zeile
Code:
pan_mausitem.bmap = fallengelassener_gegenstand.inventar_bild;

auskommentiert habe. Also müsste die Zuweisung auf "fallengelassener_gegenstand" im Event nicht richtig sein, aber da bin ich mit meinem Latein am Ende.

Code:
typedef struct
{
	ENTITY* ent_entity;	// der Entitypointer auf das Objekt im 3D-Raum, das gedroppt wurde
	BMAP* inventar_bild;
}gegenstand;

function testGegenstand_event()
{
	gegenstand* fallengelassener_gegenstand = (gegenstand*)my->skill[99];
	pan_mausitem.bmap = fallengelassener_gegenstand.inventar_bild;
}
function testGegenstand()
{
	while(enet_ent_globpointer(my) == ANET_ERROR) {wait(1);}
	my.event = testGegenstand_event;
	my.emask |= ENABLE_CLICK;
	while(1) {my.pan += 3 * time_step; wait(1);}
}

function gegenstandstest()
{
	while(key_f==0){wait(1);} while(key_f){wait(1);}
	
	gegenstand* fallengelassener_gegenstand = malloc(sizeof(gegenstand));
	fallengelassener_gegenstand->ent_entity = enet_ent_create("temp_ent.mdl",vector(random(300),random(300),0),"testGegenstand");
	fallengelassener_gegenstand->inventar_bild = "pct_item_testgegenstand.tga";
	fallengelassener_gegenstand->ent_entity.skill[99] = (int)(void*)fallengelassener_gegenstand;
}



Re: [solved] struct-Name per Laufzeit erstellen [Re: JoGa] #383303
09/18/11 19:36
09/18/11 19:36
Joined: Jul 2008
Posts: 894
T
TechMuc Offline
User
TechMuc  Offline
User
T

Joined: Jul 2008
Posts: 894
Hey,

Quote:
1. Frage: Warum benötigt man das "void"? Das int erscheint mir nach deiner Erklärung logisch, doch das "void" kann ich mir nicht erklären (was es bedeutet [Funktionstyp ohne Rückgabewert] weis ich einiger maßen), doch verstehe ich die Bedeutung in deinem Kontext nicht.

Generell sollte das void* unnötig sein, verdeutlicht aber den typecasting vorgang.
Für einen Compiler ist void ein synonym für "unbekannt". Das heißt das ein Zeiger auf "unbekannt" auf jeden beliebigen Datensatz, beliebiger Größe zeigen kann. Warum schreibe ich das hier dazu? Wenn du schreibst:
ent->skill[0] = (var)pointer;
Können bestimmte Compiler auf die Idee kommen zu sagen, dass das so wohl nicht stimmt. Weil: Ein Pointer ist erstmal kein var. Nun sucht der Compiler ob er einen Pointer in eine var automatisch umrechnen kann. Hier gibt es jetzt drei Ergebnismöglichkeiten:
1) Er erkennt, das var größe = pointer größe und schreibt den Pointer 1:1 in ent->skill[0] - gut!
2) Er kennt keine Regel um einen Pointer in eine var zu schreiben und gibt einen Syntaxfehler aus (in die Richtugn: No conversion defined between var and ENTITY*).
3) Er kennt eine Regel die aber falsch ist bzw. hier nicht passt und schreibt einen falschen Wert in ent->skill[0].

Um alle Probleme zu vermeiden typecaste ich hier zweimal (wie gesagt, vermutlich NICHT notwendig). Als erstes wird dem Compiler mitgeteilt, dass die ENTITY* Struktur in echt ein Pointer auf etwas unbekanntes ist (void*). Der Compiler "vergisst" also, was an der Stelle von ENTITY* hinzeigt überhaupt steht. Für den compiler ist das jetzt wirklich nur noch n Zeiger auf einen unbekannten Wert. Danach kommts zu einem typecast zu (int). Das akzeptiert der Compiler ganz sicher (und schreibt exakt die 4 bytes des pointers in die var), da er ja gar nicht weiß was (void*) eig ist.. daher kann er auch keine falschen Regeln anwenden bzw. motzen..


Zu deinem Code:
1) genau so wirds gemacht
2) fallengelassener_gegenstand->inventar_bild = "pct_item_testgegenstand.tga";
ist falsch.

Code:
fallengelassener_gegenstand->inventar_bild = bmap_create("pct_item_testgegenstand.tga");



ist richtig..

Re: [solved] struct-Name per Laufzeit erstellen [Re: TechMuc] #383304
09/18/11 19:38
09/18/11 19:38
Joined: Jul 2008
Posts: 894
T
TechMuc Offline
User
TechMuc  Offline
User
T

Joined: Jul 2008
Posts: 894
Wobei du hier übrigens wieder siehst wie behindert der Lite-C Compiler ist.

Zu versuchen einen Character String ("blbla") einen pointer zuzuweisen darf niemals ohne fehlermeldung durchgehen.. Naja egal..

Re: [solved] struct-Name per Laufzeit erstellen [Re: TechMuc] #383306
09/18/11 20:34
09/18/11 20:34
Joined: Dec 2003
Posts: 988
Germany, Magdeburg
JoGa Offline OP
User
JoGa  Offline OP
User

Joined: Dec 2003
Posts: 988
Germany, Magdeburg
Vielen Dank für deine Erklärungen!
Woher weist du das? Hast du schon selbst einen Compiler programmiert oder aus welchem Buch hast du das alles gelernt?
Auf jeden Fall vielen Dank für deine Mühe und Zeit, die du dir nimmst!

Deine Verbesserung leuchtet mir ein - hab ich in den bmap* Pointer ja einfach nur einen Dateinamen einspeichern wollen.
bmap_create gibt einen Pointer, daher muss ich das nehmen.
Aber leider wird mir trotzdem ein script-crash präsentiert, sobald die event-Funktion aufgerufen wird.
Ich habe nach dem Motto "Jugend forscht"
Code:
pan_mausitem.bmap = (BMAP*)fallengelassener_gegenstand.inventar_bild;

sicherheitshalber das (BMAP*) noch hinzugefügt, jedoch ohne daraus resultierende Verbesserung. Der Crash passiert trotzdem.

pan_mausitem ist ein Panel, dessen Hintergrundbild ja ein bmap-Pointer sein sollte, das müsste ja in meinem Code stimmen.
Skill 99 wird bisher nicht benutzt/überschrieben.

Hättest du noch eine Idee, wo eine Fehlerquelle sein könnte?

edit: ein
Code:
fallengelassener_gegenstand->ent_entity->tilt += 3;

im Event hat die selbige Fehlermeldung produziert, also irgendwie muss an der Zuweisung
Code:
gegenstand* fallengelassener_gegenstand = (gegenstand*)my->skill[99];

falsch laufen, ich weis nur noch nicht, was

Last edited by JoGa; 09/19/11 01:28.
Re: [solved] struct-Name per Laufzeit erstellen [Re: JoGa] #383332
09/19/11 03:46
09/19/11 03:46
Joined: Jul 2008
Posts: 894
T
TechMuc Offline
User
TechMuc  Offline
User
T

Joined: Jul 2008
Posts: 894
Überprüfe ich morgen mal - das was ich da geschrieben habe ist eig. standard c verhalten - kann sein, dass litec was anderes will.

Gruß,
Timo

Re: [solved] struct-Name per Laufzeit erstellen [Re: TechMuc] #383455
09/20/11 18:29
09/20/11 18:29
Joined: Dec 2003
Posts: 988
Germany, Magdeburg
JoGa Offline OP
User
JoGa  Offline OP
User

Joined: Dec 2003
Posts: 988
Germany, Magdeburg
OH, MEIN GOTT!
ES KLAPPT :-D

und zwar habe ich nun bei
Code:
fallengelassener_gegenstand->ent_entity.skill[99] = (int)(void*)fallengelassener_gegenstand;



Code:
fallengelassener_gegenstand->ent_entity.skill[99] = (int*)fallengelassener_gegenstand;


den Pointer-* bei der int gesetzt und das void gelöscht, nun klappt es :-)

VIIIIEEEELEN Dank (vorerst crazy) für deine Eselsgeduld und Erklärungen!
Ich werd dann mal mit den Gegenständen so weitermachen und schaun, wie weit ich komme :-)

Last edited by JoGa; 09/20/11 18:30.
Re: [solved] struct-Name per Laufzeit erstellen [Re: JoGa] #383465
09/20/11 19:43
09/20/11 19:43
Joined: Jul 2008
Posts: 894
T
TechMuc Offline
User
TechMuc  Offline
User
T

Joined: Jul 2008
Posts: 894
Hmm klar.. da siehst du genau was ich gemeint habe.. Wenn du zu (int) konvertierst, dann versucht der Compiler die (int) dem (var) - also dem Skill - zuzuweisen. Der Compiler kennt eine Konvertierungsregel zwischen int und var (left shift by 10) und zerstört deinen schönen Pointer.

Wenn du zu (int*) castest, kennt der Compiler keine Regel und lässt deinen Pointer in ruh.

Last edited by TechMuc; 09/20/11 19:45.
Re: [solved] struct-Name per Laufzeit erstellen [Re: TechMuc] #383610
09/22/11 15:01
09/22/11 15:01
Joined: Dec 2003
Posts: 988
Germany, Magdeburg
JoGa Offline OP
User
JoGa  Offline OP
User

Joined: Dec 2003
Posts: 988
Germany, Magdeburg
noch eine kleine prinzipielle Frage hab ich:

Prinzipiell sollten genauso viele free()-Befehle wie malloc()-Befehle aufgerufen werden, um keinen "Memory Leak" zu produzieren - richtig?

...ist ja eigentlich logisch, aber sicherheitshalber frag ich mal nach, um Gewissheit zu bekommen :-/

Last edited by JoGa; 09/22/11 15:23.
Re: [solved] struct-Name per Laufzeit erstellen [Re: JoGa] #383628
09/22/11 20:04
09/22/11 20:04
Joined: Jul 2008
Posts: 894
T
TechMuc Offline
User
TechMuc  Offline
User
T

Joined: Jul 2008
Posts: 894
richtig

Page 2 of 3 1 2 3

Moderated by  HeelX, Lukas, rayp, Rei_Ayanami, Superku, Tobias, TWO, VeT 

Gamestudio download | chip programmers | Zorro platform | shop | Data Protection Policy

oP group Germany GmbH | Birkenstr. 25-27 | 63549 Ronneburg / Germany | info (at) opgroup.de

Powered by UBB.threads™ PHP Forum Software 7.7.1