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.