Thread: Struct with a ptr to a dynamically allocated array of other structures :(

  1. #1
    null
    Join Date
    Dec 2005
    Posts
    18

    Struct with a ptr to a dynamically allocated array of other structures :(

    I can't really make that description any simpler. I need help accessing a certain element of a dynamically allocated array of structures, and my only access to the array is a pointer contained in another structure. Here are the structures:
    Code:
    struct Country
    {
    	int	continent;
    	char * name;
    	char * cap;
    	char * lang;
    };
    
    struct Database
    {
    	struct Country * list;
    	int ncountries;
    	char * filename;
    };
    And here is my function that needs to find the first "empty" element of the array:
    Code:
    //insert_database: insert ctry into the right element of db list
    void insert_database( struct Database * db, struct Country * ctry )
    {
    	int insertAt;
    
        if( db != NULL && ctry != NULL )
        {
    		for( int i = 0, insertAt = 0; i < db->ncountries; i++ )
    		{
    			if( db[i].list == NULL )
    			{
    				insertAt = i;
    				printf( "Insert here: %d\n", insertAt );
    
    				//copy_country( db, ctry, i );
    
    				break;
    			}
    		}
        }
    }
    Help?! I've been browsing the internet for help on this, and I can't find anything. This is confusing the crap out of me.

  2. #2
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,412
    What error message do you get?
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  3. #3
    null
    Join Date
    Dec 2005
    Posts
    18
    I don't get one. It inserts one country at index 2, and then it stops (I have 4 countries to be inserted).

  4. #4
    ATH0 quzah's Avatar
    Join Date
    Oct 2001
    Posts
    14,826
    So are you actually initializing the items you allocate to have NULL pointers, or are you just praying they randomly have that value?


    Quzah.
    Hope is the first step on the road to disappointment.

  5. #5
    null
    Join Date
    Dec 2005
    Posts
    18
    Well, that's where I'm stuck. I dynamically allocate (with calloc) the space needed for my array of country structures:
    Code:
    db->list = (struct Country *)calloc( countries, sizeof( struct Country ) );
    After that, I don't really know how to work with that array anymore. I've never read or used anything that does this. Sorry

  6. #6
    null
    Join Date
    Dec 2005
    Posts
    18
    Some of the code provided by my teacher makes me doubt what I'm doing (not that I was ever sure):
    Code:
    	for( i = 0; i < db->ncountries; i++ )
    	{
    		delete_country( db->list+i );   // free mem alloc to a country
    	}
    I should be able to use that same "db->list+i" part to check the array, right? I'm just trying to find the first free element to insert my country into...
    Data structures are so much easier, but I'm stuck with these parameters!

  7. #7
    Registered User
    Join Date
    Nov 2005
    Posts
    95
    After that, I don't really know how to work with that array anymore. I've never read or used anything that does this
    But anyway , using calloc , the space is initializied to zero.

    to loop the array :
    Code:
    int  i;
    
    for( i = 0; i < db->ncountries; i++ )
        db->list[i] = NULL;
        /* or
        *(db->list+i) = NULL;
        */

    Why don't u post the all code ?

  8. #8
    ATH0 quzah's Avatar
    Join Date
    Oct 2001
    Posts
    14,826
    Zero does not necessarily equate a NULL.


    Quzah.
    Hope is the first step on the road to disappointment.

  9. #9
    null
    Join Date
    Dec 2005
    Posts
    18
    Hi,
    I know this is late, but it's on this same topic. Your assistance so far has been really great.
    I'm stuck on validating the "location-to-insert" in the list. Here's what I have:
    Code:
    //insert_database: insert ctry into the right element of db list
    void insert_database( struct Database * db, struct Country * ctry )
    {
    	int i, insertAt = 0;
    
        if( db != NULL && ctry != NULL )
        {
    		for( i = 0; i < db->ncountries; i++ )
    		{
    			if( /*validate location*/ )
    				insertAt = i;
    		}
    
    		//printf( "Insert at: %d\n", insertAt );
    		copy_country( db, ctry, insertAt );
        }
    }
    The rest of the code, if needed:
    Code:
    #include <stdlib.h>
    #include <stdio.h>
    #include <malloc.h>
    #include <string.h>
    
    struct Country
    {
    	int	continent;
    	char * name;
    	char * cap;
    	char * lang;
    };
    
    struct Database
    {
    	struct Country * list;
    	int ncountries;
    	char * filename;
    };
    
    /***** Function Prototypes *****/
    struct Database * init_database( char filename[] );
    void fill_database( struct Database * db );
    struct Country * init_country( int continent, char * name, char * capital, char * languages );
    void insert_database( struct Database * db, struct Country * ctry );
    void copy_country( struct Database * db, struct Country * ctry, int elem );
    
    void print_database( struct Database * db );
    void print_country( struct Country * country );
    
    void delete_database( struct Database * db );
    void delete_country( struct Country * ctry );
    
    void FileHandlingFailed( const char * filename );
    
    /***** Function Definitions *****/
    int main()
    {
    	struct Database * db;
    
     	if( (db = init_database( "lab.txt" )) != NULL )
    	{
    		fill_database( db );
    		//print_database( db );
    		//delete_database( db );
    		//free( db );
    	}
    	else
    	{
    		printf( "Memory allocation procedure failed. Application closing...\n\n" );
    
    		return 1;
    	}
    
    	return 0;
    }
    
    //init_database: initialize the Database struct
    struct Database * init_database( char filename[] )
    {
    	FILE * input;
    	struct Database * db;
    	int countries = 0, i;
    	char c;
    
    	if( (input = fopen( filename, "r" )) != NULL )
    	{
    		while( (c = fgetc( input )) != EOF )
    		{
    			if( c == '\n' )
    				countries++;
    		}
    
    		db = (struct Database *)malloc( (countries * sizeof( struct Country )) );
    
    		if( db != NULL )
    		{
    			db->list = (struct Country *)calloc( countries, sizeof( struct Country ) );
    
    			if( db->list != NULL )
    			{
    				db->ncountries = countries;
    				db->filename = filename;
    			}
    		}
    
    		fclose( input );
    	}
    	else
    	{
    		FileHandlingFailed( filename );
    		
    		return NULL;
    	}
    
    	return db;
    }
    
    //fill_database: fills the Database country list with stuff from file
    void fill_database( struct Database * db )
    {
    	FILE * input;
        struct Country * tempCtry;
    	int cont;
    	char name[81], capital[81], lang[81];
    	char c;
    
    	if( (input = fopen( db->filename, "r" )) != NULL )
    	{
    		while( fscanf( input, "%d,%[^,],%[^,],%[^\n]", &cont, name, capital, lang ) != EOF )
    		{
    			if( (tempCtry = init_country( cont, name, capital, lang )) != NULL )
    				insert_database( db, tempCtry );
    			else
    			{
    				printf( "Memory allocation failed in init_country function. Aborting...\n" );
    				break;
    			}
    		}
    
    		fclose( input );
    	}
    	else
    	{
    		FileHandlingFailed( db->filename );
    
    		return;
    	}
    }
    
    //init_country: allocates memory for a new Country structure and its contents
    struct Country * init_country( int continent, char * name, char * capital, char * languages )
    {
        struct Country * ctryStruct;
    
        ctryStruct = (struct Country *)malloc( sizeof( struct Country ) );
    
        if( ctryStruct != NULL )
        {
            //country continent
            ctryStruct->continent = continent;
    
    		//init name ptr
    		ctryStruct->name = (char *)calloc( strlen( name ) + 1, sizeof( char ) );
    
            if( ctryStruct->name != NULL )
    		{
    			//copy in ctry name
    			strcpy( ctryStruct->name, name );
    
    			//init cap ptr
    			ctryStruct->cap = (char *)calloc( strlen( capital ) + 1, sizeof( char ) );
    
    			if( ctryStruct->cap != NULL )
    			{
    				//copy in ctry capital
    				strcpy( ctryStruct->cap, capital );
    
    				//init lang ptr
    				ctryStruct->lang = (char *)calloc( strlen( languages ) + 1, sizeof( char ) );
    
    				if( ctryStruct->lang != NULL )
    				{
    					//copy in ctry languages
    					strcpy( ctryStruct->lang, languages );
    
    					//printf( "%d, %s, %s, %s\n", ctryStruct->continent, ctryStruct->name, ctryStruct->cap, ctryStruct->lang );
    				}
    				else
    				{
    					free( ctryStruct->cap );
    					free( ctryStruct->name );
    					free( ctryStruct );
    
    					ctryStruct = NULL;
    				}
    			}
    			else
    			{
    				free( ctryStruct->name );
    				free( ctryStruct );
    
    				ctryStruct = NULL;
    			}
    		}
    		else
    		{
    			free( ctryStruct );
    
    			ctryStruct = NULL;
    		}
    	}
    
        return ctryStruct;
    }
    
    //insert_database: insert ctry into the right element of db list
    void insert_database( struct Database * db, struct Country * ctry )
    {
    	int i, insertAt = 0;
    
        if( db != NULL && ctry != NULL )
        {
    		for( i = 0; i < db->ncountries; i++ )
    		{
    			if( /*validate location*/ )
    				insertAt = i;
    		}
    
    		//printf( "Insert at: %d\n", insertAt );
    		copy_country( db, ctry, insertAt );
        }
    }
    
    //copy_country: performs insertion of the ctry struct into the db list
    void copy_country( struct Database * db, struct Country * ctry, int elem )
    {
    	db->list[elem] = *ctry;
    }
    
    //print_database: prints all the Countries
    void print_database( struct Database * db )
    {
    	int i;
    
    	for( i = 0; i < db->ncountries; i++ )
    	{
    		print_country( db->list+i );
    	}
    	
    	printf( "Total countries: %d\n", db->ncountries );
    
    	return;
    }
    
    //delete_database: frees allocated memory in array of Countries
    void delete_database( struct Database * db )
    {
    	int i;
    
    	for( i = 0; i < db->ncountries; i++ )
    	{
    		delete_country( db->list+i );   // free mem alloc to a country
    	}
    
    	free( db->list );  // free array
    	free( db->filename );
    }
    
    //delete_country: frees memory for Country structures
    void delete_country( struct Country * ctry )
    {
        free( ctry->name );
        free( ctry->cap );
        free( ctry->lang );
    }
    
    
    //print_country: prints a Country structure
    void print_country( struct Country * country )
    {
    	printf("%-23s%-20s%-25s\n", country->name, country->cap, country->lang );
    }
    
    //FileHandlingFailed: prints the error msg when program fails to load file
    void FileHandlingFailed( const char * filename )
    {
    	printf( "File handling procedure failed: unable to locate file %s. Application closing...\n\n", filename );
    }
    I've tried a few different things to validate the list index, but nothing works. Compiler won't let me do these things:
    • db->list[i] == NULL
    • db->list[i] == 0
    It says these comparisons don't work for structs. Is there any way to check if db->list[i] is not pointing to something?

  10. #10
    ATH0 quzah's Avatar
    Join Date
    Oct 2001
    Posts
    14,826
    db->list[ x ] is the instance of a structure. You are, by using [ ], dereferencing the pointer, to give you the structure itself. You cannot see if a structure is null. You have to check to see if the pointer to the structure is null.

    Perhaps you should change list to be an array of pointers to structures instead.

    [edit]
    There's really no way to do what you're asking easily. If all you have is a pointer to a series of objects, there is no way to know when you reach the end of those objects, unless they have speficically allocated an extra pointer, and set it to null.

    Even that is painful to attempt. Basically you need, not a pointer to an object, but a pointer to a pointer to an object. If that turns up NULL, then you have a pointer which doesn't point to an object. There is no way to allocate a block of N objects, and have the "N+1" object be null. Malloc won't do it.

    What it sounds like you want is a "string" of objects, where the final one is the null character. Well, consider strings. Even if you encounter a 'null character', that is still an instance of a character. It's just a character you use to compare against to stop your looping. It would be the same as if you had chosen the letter 'a' to stop on. There is still a character there. You can't run through a string and stop when you reach a character that isn't there, because as far as it's concerned, there's always a character there. You just pick some value to stop on. Basically, you create enough room to set one character to "null", and stop when you reach it.

    You will need to have done the same thing with your array of structures. Have one more on the end always set to some value, and stop when you find it.

    The best you could get would be something like:
    Code:
    int checknull( struct list *l )
    {
        if( l->contient == foo && l->name == NULL && l->cap == NULL && l->lang == NULL )
            return at_the_end_value;
    
         return not_at_the_end_value;
    }
    This of course means when you allocate your structures, you have to set all of these values to match up just-so. (Or use only one of them, or whatever you choose as a terminator.)
    [/edit]

    Quzah.
    Last edited by quzah; 05-18-2006 at 11:11 PM.
    Hope is the first step on the road to disappointment.

  11. #11
    null
    Join Date
    Dec 2005
    Posts
    18
    Thanks Quzah. That really really helps. I rewrote my insert_database function, implementing what you suggested. Here's what I ended up with:
    Code:
    void insert_database( struct Database * db, struct Country * ctry )
    {
    	int i, insertAt = -1;
    	struct Country * elem;
    
        if( db != NULL && ctry != NULL )
        {
    		for( i = 0; i < db->ncountries && insertAt < 0; i++ )
    		{
    			if( (db->list+i)->continent == 0 )
    			{
    				elem = db->list+i;
    				insertAt = i;
    			}
    		}
    
    		copy_country( elem, ctry );
        }
    }
    This works excellently. Sorry for my inability to explain what I wanted to do. It was equally hard for me to understand what was happening, but I get it now, so thanks.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Replies: 16
    Last Post: 05-29-2009, 07:25 PM
  2. Help calling function is asm
    By brietje698 in forum C++ Programming
    Replies: 24
    Last Post: 12-06-2007, 04:48 PM
  3. Replies: 6
    Last Post: 01-16-2007, 09:21 PM
  4. Unknown Memory Leak in Init() Function
    By CodeHacker in forum Windows Programming
    Replies: 3
    Last Post: 07-09-2004, 09:54 AM
  5. Hi, could someone help me with arrays?
    By goodn in forum C Programming
    Replies: 20
    Last Post: 10-18-2001, 09:48 AM