?
Solved

structure array problem/question

Posted on 2003-03-03
22
Medium Priority
?
254 Views
Last Modified: 2010-04-17
hello all

I am having problems with an idea regarding an App I am writing, and how to make use of a structure array.

Here is my question(s)...

I want to define a structure like
struct entity {
    char *entityKey;
    char *entityValue;
} *myentity;

Now I want to read in any number of entityKey, entityValue pairs.... Since the number of key/value pairs can vary from run to run of the application I do not want to statically define an upper limit on the size of my structure array, thus I would like to dynamically allocate it as I need it... make sense??? I hope...

Thus here are my questions is the above structure valid? if so how can I add a key/value pair to it? and then increment to the next point in the array for the next value/key pair... and then how do i read them.. how do I cycle through the structure once I have filled it up...

The goal is to not dimension the size of the structure array... How can I do this???

pseudo code or an example would be great...

any suggestions???

thanks all

echardrd
0
Comment
Question by:echardrd
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 12
  • 4
  • 3
  • +2
22 Comments
 
LVL 6

Expert Comment

by:gj62
ID: 8060465
There are a few ways.  If you want to stay within the concept of an array, you can do the following (if not, you can also do a linked list)...

struct entity {
   char *entityKey;
   char *entityValue;
};

struct entity *myentity;

/* create first struct */
myentity = (entity *)realloc(NULL, sizeof(entity)*numEntities);

/* if you need to make it larger later on, just call realloc again in this way */

myentity = (entity *)realloc(myentity, sizeof(entity) * newNumEntities);

/* you can then access it like... */
myentity[2].entityKey;

which would give you the 3rd element in the array and access the entityKey member

WARNING - you don't have any storage associated with your members, entityKey or entityValue, so unless you are malloc'ing space for them, you can't store a string to those members, only a pointer...
0
 
LVL 46

Expert Comment

by:Kent Olsen
ID: 8060496

Pointers to Pointers.  The most confusing thing in C.

In your struct definition, myentity is not a pointer, no '*' is necessary.

With that out of the way:

/*
  I prefer to add 'typedef' to struct definitions.
  It makes the source code 'cleaner'.
*/

typedef struct
{
   char *entityKey;
   char *entityValue;
} myentity;


myentity **EntityList = NULL;
int      EntityCount = 0;

/*
   Create a new entity.
   Assumes that storage for Key and Value has already
   been assigned and we're just dealing with the pointers
*/

myentity *CreateEntity (char *Key, char *Value)
{
  myentity *NewEntity;

/*  Create the new entity  */

  NewEntity = (myentity *)malloc (sizeof (myentity));
  NewEntity->entityKey = Key;
  NewEntity->entityValue = Value;

  EntityCount++;

/*  Extend the entity table by one entry  */

  EntityList = (myentity **)realloc (EntityList, EntityCount * sizeof (myentity *));

/*  Add the NewEntity to the bottom of the table  */

  *EntityList[EntityCount-1] = NewEntity;

  return NewEntity;
}


You can ALWAYS scan the entity table with the following:

  for (idx = 0; idx < EntityCount; idx++)
    reference: EntityList[idx]->Key || EntityList[id]->Value


Good Luck!

Kdo
0
 
LVL 6

Expert Comment

by:gj62
ID: 8060513
First, my comment is wrong - in the first realloc, you are
creating your first array of entities, not just one.  The number of entities is whatever numEntities is...

To elaborate on my warning...

If you know the size of entityKey and entity value, you should define your struct as follows:

struct entity {
  char entityKey[11];
  char entityValue[11];
};

In this example, both entityKey and entityValue can hold 10 character strings (plus one for the null).

If you don't know the size at compile time, then you have to malloc, as follows:

struct entity {
  char *entityKey;
  char *entityValue;
};

struct entity *myentity;

/* create array of structs */
myentity = (entity *)realloc(NULL, sizeof(entity)*numEntities);

/* allocate string space */
for( i = 0; i<numEntities; ++i)
{
  myentity[i].entityKey = (char *)malloc(sizeofEntityKey);
  myentity[i].entityValue = (char *)malloc(sizeofEntityValue);
}

You can then store strings of up to sizeofEntityKey-1 and sizeofEntityValue-1 (you need the last byte for the null...)
0
Get real performance insights from real users

Key features:
- Total Pages Views and Load times
- Top Pages Viewed and Load Times
- Real Time Site Page Build Performance
- Users’ Browser and Platform Performance
- Geographic User Breakdown
- And more

 
LVL 6

Expert Comment

by:gj62
ID: 8060584
Kdo -

why bother with NewEntity at all?  Why not just have myentity point to an array of structs (like you did) and access them directly?  Seems unnecessarily complex, and a waste of memory - since you are actually allocating 2 struct everytime you call CreateEntity (one with a malloc, and one with realloc...)

modified code below:::

typedef struct
{
  char *entityKey;
  char *entityValue;
} myentity;


myentity *EntityList = NULL;
int      EntityCount = 0;

/*
  Create a new entity.
  Assumes that storage for Key and Value has already
  been assigned and we're just dealing with the pointers
*/

myentity * CreateEntity (char *Key, char *Value)
{
 EntityCount++;

/*  Extend the entity table by one entry  */

 EntityList = (myentity *)realloc (EntityList, EntityCount * sizeof (myentity *));

/*  Fill values  */
 EntityList[EntityCount].entityKey = Key;
 EntityList[EntityCount].entityValue = Value;

 return EntityList;
}

Returning EntityList is redundant, since it would be modified as a global anyhow, or could never be accessed within the function.  
0
 
LVL 6

Expert Comment

by:gj62
ID: 8060595
Oops - you either need to move EntityCount++; to just before the return statement, or access the array element using EntityCount-1...

sorry...
0
 
LVL 6

Expert Comment

by:gj62
ID: 8060621
A bit of symantic explanation -

In my code,

myentity is the first element in an array of structures.

Since in C, array-of-T decays into a pointer to its first element, it is common, though slightly incorrect, to call this a "pointer to the array", when in fact it is the first element of the array.

In Kdo's code, you actually do get a pointer to an array, though you don't need that extra level of indirection...
0
 
LVL 46

Expert Comment

by:Kent Olsen
ID: 8060666
Hi Gj62,

Both work just fine.  I'm so used to dealing with pointers to pointers that I instinctively code that way.

In the problem posed here, a table of "entity"s is probably the way to go, just as you suggest.  But if "entity" grows such that it contains more and more fields, then a table of pointers is much more flexible.  (It sorts faster, relocates quicker, etc.  When the table grows to thousands of values every relocation is significant.  And if the table is thousands of values of hundreds of bytes, every relocation of a table of structs can move a megabyte or more .)

Do note that EntityList is a table of pointers (sizeof (*)) and not a table of entities (sizeof (entity)).  My algorithm sacrifices a small amount of storage (one pointer per entity) for speed.


Kdo
0
 
LVL 6

Expert Comment

by:gj62
ID: 8060732
Caught this just as I was going to write and say,

Oops, Kdo is just keeping an extra pointer per table...

Sorry for the error, and thanks for the explanation - makes good sense to me...
0
 

Author Comment

by:echardrd
ID: 8061041
guys... thanks for the quick responses... I have been trying to implement your suggestions using the following simple "wrapper" (forgive its simple minded nature.. just quick prototype). In any event what happens, is that everytime I access the CreateEntity function,  and print out key value pairs, it seems like the last set of key value pairs overwrites all previous ones...

i.e. [0][0] = key1, val1
     [1][1] = key2, val2

now when I print out the values for 0,0 and 1,1 i get key2, val2 for both responses... ideas.. am I doing something stupid here?? and by the way I am going to award both of you pints since I was dumb enough for posting this question twice.

here is my sample code...



#include <stdio.h>
#include <string.h>
#include <malloc.h>

typedef struct
{
  char *entityKey;
  char *entityValue;
} myentity;


myentity **EntityList = NULL;
int      EntityCount = 0, i = 0;

/*
  Create a new entity.
  Assumes that storage for Key and Value has already
  been assigned and we're just dealing with the pointers
*/

myentity *CreateEntity (char *Key, char *Value)
{
 myentity *NewEntity;

/*  Create the new entity  */

 NewEntity = (myentity *)malloc (sizeof (myentity));
 NewEntity->entityKey = Key;
 NewEntity->entityValue = Value;

 EntityCount++;

/*  Extend the entity table by one entry  */

 EntityList = (myentity **)realloc (EntityList, EntityCount * sizeof (myentity *));

/*  Add the NewEntity to the bottom of the table  */

 EntityList[EntityCount-1] = NewEntity;
  for (i = 0; i < EntityCount; i++)
  {
     fprintf(stdout, "%d.\tKEY: %s\t%VALUE: %s\n", EntityList[i]->entityKey, EntityID->entityValue);

 return NewEntity;
}

main()
{
  char ph[128], val[128];
  while(strcmp(ph, "done") != 0)
  {
    printf("\nenter key: ");
    gets(ph);
    if (strcmp(ph, "done") == 0)
      break;
    printf("enter value: ");
    gets(val);
    CreateEntity(ph, val);
  }
}
 for (idx = 0; idx < EntityCount; idx++)
   reference: EntityList[idx]->Key || EntityList[id]->Value

0
 

Author Comment

by:echardrd
ID: 8061046
forgot to add a closing } in the "for loop" for the CreateEntity function...
0
 
LVL 6

Expert Comment

by:gj62
ID: 8061217
Both Kdo and I mentioned that your structure does not have storage for the key and value strings, only pointers.

In other words, each time you call CreateEntity, you are passing POINTERS to the strings.  Your structure is saving the pointers, not the actual strings themselves.  So each time you call CreateEntity, you are passing in the same pointers (for ph and val).  In fact, if you change ph and val, you will notice that anytime you access Key and Value, they now have the new strings!  Ah, such are the wonders of pointers.

This of it this way.  You have allocated storage for only 2 strings, ph and val.  Each of those strings has an address in memory.  What you are storing in Key and Value are the addresses to ph and val, not a copy of those strings, so whenever ph and val change, any reference to those addresses also appears to change.

Here are the modifications you need to make to "keep" all the values.

Change your struct to:

typedef struct
{
 char entityKey[128];
 char entityValue[129];
} myentity;


and change your assignment within CreateEntity from

NewEntity = (myentity *)malloc (sizeof (myentity));
NewEntity->entityKey = Key;
NewEntity->entityValue = Value;

to

NewEntity = (myentity *)malloc (sizeof (myentity));
strcpy(NewEntity->entityKey,Key);
strcpy(NewEntity->entityValue,Value);

This will allocate 2 strings for EACH structure in your array.  Each string will have 128 bytes - 127 bytes for character storage, and 1 byte for the null.

This is wasteful if you will have alot of structures and small strings.  If you wanted to dynamically allocate your string storage, you could do the following:

Keep your struct as:

typedef struct
{
 char *entityKey;
 char *entityValue;
} myentity;

and change your assignments within CreateEntity to:

NewEntity = (myentity *)malloc (sizeof (myentity));
/* you need to add 1 because strlen "forgets" the null...*/
NewEntity->entityKey = (char *)malloc(strlen(Key+1));
strcpy(NewEntity->entityKey, Key);
NewEntity->entityValue = (char *)malloc(strlen(Value+1));
strcpy(NewEntity->entityValue, Value);

That will allocate only the storage that you need...
0
 

Author Comment

by:echardrd
ID: 8061472
thanks guys...

the last suggestion worked great... now I tried something like the following... why doesn't this work.. just curious.. was trying to eliminate the need to redefine a "temporary" structure i.e. NewEntity in previous examples...

#include <stdio.h>
#include <string.h>
#include <malloc.h>

typedef struct
{
      char *entityKey;
      char *entityValue;
} myentity;


myentity **EntityList = NULL;
int      EntityCount = 0, i = 0;

/*
 Create a new entity.
 Assumes that storage for Key and Value has already
 been assigned and we're just dealing with the pointers
*/

myentity *CreateEntity (char *Key, char *Value)
{
     EntityCount++;
     EntityList = (myentity **)realloc (EntityList, EntityCount * sizeof (myentity *));
     EntityList[EntityCount - 1]->entityKey = (char *) malloc (strlen(Key) + 1);
     strcpy(EntityList[EntityCount - 1]->entityKey, Key);
     EntityList[EntityCount - 1]->entityValue = (char *) malloc (strlen(Value) + 1);
     strcpy(EntityList[EntityCount - 1]->entityValue, Value);

     return EntityList[EntityCount-1];
}

main()
{
     char ph[128], val[128];
 
     while(strcmp(ph, "done") != 0)
     {
        printf("\nenter key: ");
        gets(ph);
        if (strcmp(ph, "done") == 0)
           break;
        printf("enter value: ");
        gets(val);
        CreateEntity(ph, val);          
     }
     for (i = 0; i < EntityCount; i++)
     {
          fprintf(stdout, "%d.\tKEY: %s\t%VALUE: %s\n", i, EntityList[i]->entityKey, EntityList[i]->entityValue);
     }
}

here is the code that works like a charm...

how is it as far as handling memory allocation / deallocation as programmed.. and how should I free this structure that I allocated memory for.. tried free(EntityList) and even members of this structure.. doesn't work... how can I take this working code and ensure it is memory friendly...

#include <stdio.h>
#include <string.h>
#include <malloc.h>

typedef struct
{
      char *entityKey;
      char *entityValue;
} myentity;


myentity **EntityList = NULL;
int      EntityCount = 0, i = 0;

/*
 Create a new entity.
 Assumes that storage for Key and Value has already
 been assigned and we're just dealing with the pointers
*/

myentity *CreateEntity (char *Key, char *Value)
{
     myentity *NewEntity;

     /*  Create the new entity  */
     NewEntity = (myentity *)malloc (sizeof (myentity));
     /* you need to add 1 because strlen "forgets" the null...*/
     NewEntity->entityKey = (char *)malloc(strlen(Key+1));
     strcpy(NewEntity->entityKey, Key);
     NewEntity->entityValue = (char *)malloc(strlen(Value+1));
     strcpy(NewEntity->entityValue, Value);

     EntityCount++;

     /*  Extend the entity table by one entry  */

     EntityList = (myentity **)realloc (EntityList, EntityCount * sizeof (myentity *));

     /*  Add the NewEntity to the bottom of the table  */

     EntityList[EntityCount-1] = NewEntity;

     return EntityList[EntityCount-1];
}

main()
{
     char ph[128], val[128];
 
     while(strcmp(ph, "done") != 0)
     {
        printf("\nenter key: ");
        gets(ph);
        if (strcmp(ph, "done") == 0)
           break;
        printf("enter value: ");
        gets(val);
        CreateEntity(ph, val);          
     }
     for (i = 0; i < EntityCount; i++)
     {
          fprintf(stdout, "%d.\tKEY: %s\t%VALUE: %s\n", i, EntityList[i]->entityKey, EntityList[i]->entityValue);
     }
}

thanks

last question i promise...

echardrd
0
 
LVL 6

Expert Comment

by:gj62
ID: 8061958
I was wrong - Kdo's code was not allocating two structs - it was allocating a pointer to a struct and the struct itself.

Your changed code is not working because you are not allocating the necessary struct in memory when you call CreateEntity.

Now, if you want to free everything in the working code, you would do the following (I like to set pointers to NULL after I free, so I write a quick func to do that:

void freeMem(void * pMem)
{
  free(pMem);
  pMem = NULL;
}

for (i = 0; i < EntityCount; ++i)
{
  /* first, free the string space... */
  freeMem(EntityList[i]->entityKey);
  freeMem(EntityList[i]->entityValue);

  /* now free the struct... */
  freeMem(EntityList[i]);
}
/* lastly, free the array of pointers to entity structs */
freeMem(EntityList);

That should do it - I don't have a compiler on this machine, so I may have missed a typo...
0
 

Author Comment

by:echardrd
ID: 8063888
Assuming the first code segment in my last post is there a way that I could do something like this? without the need to allocate another "temporary structure" albeit it is only a pointer...?

please refer to first code segment of previous post....


thanks guys..

echardrd
0
 
LVL 6

Expert Comment

by:gj62
ID: 8064840
I changed the code from the first part - the following code now uses an array of structures, rather than an array of pointers to structures.

#include <stdio.h>
#include <string.h>
#include <malloc.h>

typedef struct
{
  char *entityKey;
  char *entityValue;
} myentity;


myentity *EntityList = NULL;
int      EntityCount = 0, i = 0;

/*
Create a new entity.
Creates storage for Key and Value
*/

void * freeMem(void * pMem)
{
  if ( pMem ) free(pMem);
  return(NULL);
}  

myentity * CreateEntity (char *Key, char *Value)
{
    EntityList = (myentity *)realloc (EntityList, (EntityCount+1) * sizeof (myentity));
    EntityList[EntityCount].entityKey = (char *) malloc (strlen(Key) + 1);
    strcpy(EntityList[EntityCount].entityKey, Key);
    EntityList[EntityCount].entityValue = (char *) malloc (strlen(Value) + 1);
    strcpy(EntityList[EntityCount].entityValue, Value);

    EntityCount++;
    return EntityList;
}

int main()
{
    char ph[128], val[128];

    while(strcmp(ph, "done") != 0)
    {
       printf("\nenter key: ");
       gets(ph);
       if (strcmp(ph, "done") == 0)
          break;
       printf("enter value: ");
       gets(val);
       CreateEntity(ph, val);          
    }
    for (i = 0; i < EntityCount; i++)
    {
       fprintf(stdout, "%d.\tKEY: %s\t%VALUE: %s\n", i, EntityList[i].entityKey, EntityList[i].entityValue);
    }

    /* free up allocated memory */
    for ( i = 0; i < EntityCount; i++)
    {
       EntityList[i].entityKey = (char *)freeMem(EntityList[i].entityKey);
       EntityList[i].entityValue = (char *)freeMem(EntityList[i].entityValue);
    }
    EntityList = (myentity *)freeMem(EntityList);
     return(0);
}
0
 

Accepted Solution

by:
rechard earned 0 total points
ID: 8084944
great guys..
0
 
LVL 6

Expert Comment

by:gj62
ID: 8085103
How come you didn't accept any of our answers?  That is a bit nasty...
0
 
LVL 6

Expert Comment

by:gj62
ID: 8085107
How come you didn't accept any of our answers?  That is a bit nasty...
0
 

Expert Comment

by:SpideyMod
ID: 8085200
echardrd,
Having multiple accounts is a violation of the membership agreement: http://www.experts-exchange.com/jsp/infoMemberAgreement.jsp 

I have removed all question points from both accounts so no further damage can be done.  An administrator will be contacting you shortly so they can discuss your options.

It is best if you come clean and indicate all the accounts you use when the administrator contacts you.

SpideyMod
Community Support Moderator @Experts Exchange
0
 

Expert Comment

by:rechard
ID: 8085640
SpideyMod my absolute sincerest apologies. It was absolutely not my intention to do this. My intent was tolet these guys know I appreciated there help and then to award points to them... Since I was smart enough to post the same question twice, and two different guys helped me gj62 and Kdo, I was going to award both of them points. gj62 on this question since he was the only one to respond here and Kdo on the other post for this question since he only responded there.. It was absolutely not my intent to do this.

Please take my word for it and distribute the points to gj62 on this post http://www.experts-exchange.com/Programming/Q_20537024.html and to
Kdo for the post http://www.experts-exchange.com/Programming/Q_20537021.html

Once again sorry for the confusion, but having these 2 accounts on 2 different machines with "remember me" selected has added to my confusion.

Sincerest regrets.
echardrd/rechard
0
 

Expert Comment

by:rechard
ID: 8085652
gj62...

As I expressed in my last post. This was an absolute unitentional mistake on my part.. My intent was to add a comment saying thanks and to award points... Became mixed up with the accounts... I have asked that the points be awarded to both you and Kdo.. I am sorry for this, and truly appreciate your help in this matter.

rechard/echardrd
0
 
LVL 6

Expert Comment

by:gj62
ID: 8088600
OK, no worries - I figured it was a mistake, since you were obviously you, and seemed sincere in your thanks...
0

Featured Post

New feature and membership benefit!

New feature! Upgrade and increase expert visibility of your issues with Priority Questions.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

In this post we will learn different types of Android Layout and some basics of an Android App.
In this post we will learn how to connect and configure Android Device (Smartphone etc.) with Android Studio. After that we will run a simple Hello World Program.
An introduction to basic programming syntax in Java by creating a simple program. Viewers can follow the tutorial as they create their first class in Java. Definitions and explanations about each element are given to help prepare viewers for future …
Viewers will learn how to properly install Eclipse with the necessary JDK, and will take a look at an introductory Java program. Download Eclipse installation zip file: Extract files from zip file: Download and install JDK 8: Open Eclipse and …
Suggested Courses

777 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question