To all those who are new to structures, or trouble with errors, or get lost in yer own spaghetti, or because you just like to read this, I present you a solution I commonly use to keep things structured with as few memory leaks, code spaghetti and unpredictable behaviour as possible.

First, an example code about structures.
Next, the explanation about structures.
Follows, the way I do it, and an explanation.

Code:
typedef struct {

  var itemNumber;
  BMAP *image; //pointer to bitmap
  String *name; //pointer to string

} Item;


typedef struct {
  
  Item item1; //no pointer, actual object!
  Item item2;

} Inventory;


void main() {
  Inventory *inv; //pointer to inventory
  inv = malloc( sizeof( Inventory ) ); //assign a new inventory
  
  //first you do after allocating a new structure, is fill all its parameters!
  //Otherwise you can end up with some nasty unpredictable errors

  //fill item1
  //arrow (->) means you call from pointer "inv" to its member "item1"
  //point (.) means you call from object "item1" to its member "itemNumber" (or "image", "name")
  inv->item1.itemNumber=0;
  inv->item1.image = bmap_create("bla");
  inv->item1.name = "bitmap of item 1";

  //same for item2
  [...]

  //for the sake of this example, let's remove this inventory again
  free( inventory ); //remove the inventory (and automatically the items!)
  
}



You see, the inventory can now be removed by free(), and it'll remove the items along automatically as they are part of the inventory structure. If the items were pointers, you had to remove the items manually before removing the inventory. Else their pointer will be lost and thus cause a memory leak.

If you look further, you see that the item structures have a bitmap and string pointer. While the item structures are removed along with the inventory, the bitmaps and strings will not! So, before removing the inventory, you will have to remove the bitmap and strings of the item structures first in order to prevent memory leaks.

As you see, this causes quite some hassle, and is pretty "dangerous" as you may easily oversee a memory leak. Your memory will slowly fill with 'floating', unreachable data structures, often freezing your game after a while, or give other unpredictable results.

To solve this requires some dedication to tidy programming. What I do, is give EVERY object I make a new file. This should be consequently done, no exceptions, to keep your code overviewable and tidy. Each objects always gets a creation function, a remove function, and possibly (commonly) a run function.


Now, another example code, the way I usually do it:

Code:
//Item.c
typedef struct {

  int itemNumber;
  BMAP *image;
  String *name;

} Item;


//a function to visualize an item on a panel
void item_visualize( Inventory *inventory, Panel *panel ) {
  panel->bmap = inventory->image;
}


//Item creation function, returns pointer to the newly created Item
Item *item_create( int number, String *imageName ) {
  
  Item *item = malloc( sizeof( Item ) );
  
  //always fill ALL the item's properties immediately after!
  item->itemNumber = number;
  item->image = bmap_create( imageName );
  item->name = imageName;
  
  return item; //return the new item back
}


//function to remove an item
void item_remove( Item *item ) {
  ptr_remove( item->image ); //remove bitmap first
  ptr_remove( item->name ); //remove name
  free( item ); //THEN remove item
}



Code:
//Inventory.c
typedef struct {
  
  bool alive;

  Panel *panel;

  Item *item1;
  Item *item2;
  
} Inventory


//function to keep the inventory running and catch input
void inventory_run( Inventory *inventory ) {
  
  //infinite loop
  //in order to check if the object still exists and prevent empty pointer errors,
  //you can best add an "alive" state, just add an "alive" boolean in the structure, and check it here
  //after the while, call the inventory_remove function to fully destroy
  //We simply make an inventory_kill() function, and set the boolean to false to kill the object
  while( inventory->alive ) { //as long as the inventory is alive, keep loopin'

    //when we press key_1, show item1
    if( key_1 ) {
      item_visualize( inventory->item1, inventory->panel ); //remember this function in the item code?
    }

    //when we press key_2, show item2
    if( key_2 ) {
      item_visualize( inventory->item2, inventory->panel );
    }

    wait( 1 );
  }

  inventory_remove( inventory ); //the object is dead, remove it!
}


//function kill
void inventory_kill( Inventory *inventory ) {
  inventory->alive = false;
}


//function to create a new inventory
Inventory *inventory_create() {

  //allocate new inventory structure
  Inventory *inventory = malloc( sizeof( Inventory ) );
 
  //once again, fill all its parameters immediately after
  inventory->alive = true; //we set this object to alive first!
  inventory->panel = pan_create("bla");
  inventory->item1 = item_create( 1, "item1.tga" ); //easily create an item
  inventory->item2 = item_create( 2, "item2.tga" );

  //in addition to the item code, the inventory code catches player input
  //this run function handles this input, and loops infinitely
  inventory_run( inventory );

  return inventory; //give back the inventory
}


void inventory_remove( Inventory *inventory ) {
  item_remove( inventory->item1 ); //remove item1 first
  item_remove( inventory->item2 ); //remove item2
  ptr_remove( inventory->panel ); //don't forget to remove the panel too
  free( inventory ); //THEN remove inventory safely
}



Code:
//main
#include "Item.c"
#include "Inventory.c"


void main() {

  //create a new inventory!
  Inventory *inventory = inventory_create();

  //main loop (a little dirty, but forgiveable for now :) )
  while( 1 ) {
    
    //when we press q, remove the inventory
    if( key_q ) {
      inventory_remove( inventory ); //this should go smooth without errors...
    }

    wait( 1 );
  }
}



The biggest fight is against pointer management. You can easily end up under heaps of empty pointer errors, or worse, random crashes that occur at unpredictable times, or even more worse, errors pointing to parts of code that aren't wrong at all! Yes, Lite-c has its bad moments, but they can be overcome.

So, quite the hassle for a bit of code that's sort of tidy and emulates a part of object orientation, but this way I discovered to be working quite well when your project gets big. Too big to handle.

All comments and critics are welcome.

Regards,
Joozey



P.s.
The code represented above is not tested at all. It's about the concept, not about giving away a piece of inventory code. IF something does not work for you and you fail to see why, feel free to comment, I shall change it in the example!

Last edited by Joozey; 07/08/09 21:08.

Click and join the 3dgs irc community!
Room: #3dgs