Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations IamaSherpa on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Object Oriented Programming in C (dynamic array allocation question)

Status
Not open for further replies.

wearyweary

Programmer
Mar 23, 2001
4
Hey guys,

I've come across quite the annoying bug in my code..

I working with a butch of "Classes" I made in C, made up of structures with data and pointers to functions. Everything is working great, but I'm having one problem with dynamically allocating an Array for an object. Heres the problem


typedef struct //define an Object with an array
{
void* anArray[50]; //array of pointers

}arrayObject;


//now we are in main

arrayObject* myArrayObject; //a pointer to an object
myArrayObject = malloc(sizeof(arrayObject)); //make memory

//this line will cause an "incompatible pointer assignment"
myArrayObject->anArray = malloc(sizeof(void*)*50);


I thing I am confused about how an array really works.. I know it should just be a pointer to the begining element of the array, but then there shouldn't be a problem making it point to the beginning of some free memory. Should I use "void** myarray;" instead of the array notation. I believe this crashed when I tryed "myarray[10]=0;"

Anyway, any help, or explaination of extact WHAT is going on when you make an array would be usefull..

Thanks,

Eric
 
myArrayObject->anArray = malloc(sizeof(void*)*50);

This is problematic because myArrayObject->anArray isn't a pointer, it's an *array* of 50 pointers! :) malloc() return a pointer to void, so the assignment must be a /pointer/ to some type and therefore can't be to an array.

IOW, what you're doing is equivalent to something like this:

int array[50];

array=malloc(sizeof(int) * 50);

it's redundant and wrong as you already have 50 ints.

You could do 2 things here, if I understand your intent:

1. leave the definition as it is and remove the offending call to malloc().

2. (as you suggested), change the type of
myArrayObject->anArray to a double pointer to void.

The 2nd option is probably going to work better for you in the long run. The problem with the 1st option is that you have an arbitrary fixed limit (50) that may change over time. OTOH, if you're absolutely sure that you will only need to store 50 objects at the maximum, then this is perfectly fine and simplifies your code in the process.

HTH,


Russ
bobbitts@hotmail.com
 
Another thing to add is that your definition of anArray as is allocates an array of 50 pointers to void. But you must allocate each of those pointers individually. So you can't do stuff like:

char a[]="something";

/* error! myArrayObject[0] points to a random address! */
memcpy(myArrayObject[0],sizeof a,1);

Until you've allocated space for the element you're writing to. Here's an approach for adding objects:

/*** untested ***/

/* redefine arrayObject to keep track of how many objects
* it currently contains
*/
typedef struct //define an Object with an array
{
void* anArray[50]; //array of pointers
int ctr;

}arrayObject;

/* you could have a function to add objects to
* your "arrayObject"
*/
int
addObject(arrayObject *obj,void *data,size_t size)
{
int error=0;

/* indexing starts at 0, so if ctr==50, the array is
* full
*/
if (ctr >= sizeof obj->anArray / sizeof *obj->anArray) {
error=2; /* overflow */
} else {
/* get memory for this object */
obj->anArray[obj->ctr]=malloc(size);
if (obj->anArray[obj->ctr]!=NULL) {
/* copy the object to the array */
memcpy(obj->anArray[obj->ctr],size,1);
++obj->ctr;
} else {
error=1; /* couldn't allocate memory */
}
}
return error;
}

Russ
bobbitts@hotmail.com
 
char a[]="something";

/* error! myArrayObject[0] points to a random address! */
memcpy(myArrayObject[0],sizeof a,1);

should be:

memcpy(myArrayObject[0],a,sizeof a,1);

AND:

memcpy(obj->anArray[obj->ctr],size,1);

should be:

memcpy(obj->anArray[obj->ctr],data,size,1);

:) ... cut me some slack, it's friday here.

Russ
bobbitts@hotmail.com
 
thanks... I figured it out.. Good suggestions..

I used

void** myArray;
myarray = malloc(sizeof (void*) * 50); //works great

I use a constuctor like function to create new arrayObject and create memory (above) for it's array of stuff..

Thanks a lot again for the help.

One thing though. I'm not sure why (even though it's wastefull) I can't use

myarray = malloc(someAmount); //error

since myarray is actually just a pointer in machine code.
I'm guessing it's to keep people from me from wasting memory like I was tyring to :)

Thanks yet again,

Eric

 
myarray = malloc(someAmount);

I'm not sure what you're getting at here, you /can/ do this as long as someAmount evaluates to an integral expression.

Or do you mean something like: Why can't I just allocate the memory all in one shot rather than allocating 50 pointers, looping to set each allocated pointer to NULL and then making each allocation separately for each object that I add?

This actually might be a reasonable strategy depending on the context. Memory allocations are generally costly, so sometimes it /is/ a good idea to allocate a bunch of memory that won't be used immediately to avoid having to do it each time you need more (i.e. each time you add an object to the array).

To expand on what we did above, consider how to deal with managing a non-fixed size array -- a double pointer. Here's a sample "constructor" function, assuming this definition of arrayObject:

/*** untested ***/

typedef struct
{
void **anArray; /* array of objects */
size_t currentSize; /* # of slots allocated */
size_t length; /* # of slots full */
} arrayObject;

arrayObject *
mkArray(size_t initialSize)
{
size_t i;
arrayObject *obj=malloc(sizeof *obj);

if (obj!=NULL) {
obj->anArray=malloc(sizeof(void *) * initialSize);
if (NULL==obj->anArray) {
free(obj);
obj=NULL;
} else {
for (i=0;i<length;++i) {
obj->anArray=NULL;
}
obj->size=initialSize;
obj->length=0;
}
}
return obj;
}

Since you have an initial size, you can only store that many objects before you need to allocate more. In your &quot;insert&quot; function, you might have some code like this:

if (obj->length>=obj->size) {
/* need to add more elements to the &quot;array&quot; */
void **tmp=realloc(obj,sizeof(void *) * (obj->size * 2));
if (tmp!=NULL) {
obj=tmp;
obj->size*=2;
} else {
/* indicate error */
}
}

/* if there was no error, add object to array here */

It's not always the best scheme to just double the size, but you get the idea.


Russ
bobbitts@hotmail.com
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top