realloc a pointer to a pointer

Started by
4 comments, last by DrTwox 15 years, 8 months ago
Hi all. I'm trying to rewrite three similar functions into one generic function. Currently I have something like this that works: (Retyped to summarise, not the full code!)

#define GROWSIZE 100
#define SQL_MAX_QUERY_SIZE 1024

/* This struct is mostly pointless now. I'm fixing a few other bits of code before I do away with it. */
typedef struct {
    unsigned char *string;
} SQL_TRANSACTION;

/* For good or bad, these are global */
SQL_TRANSACTION *SQL_inserts = NULL;
unsigned int numInserts = 0;
unsigned int numInsertsAllocated = 0;

SQL_TRANSACTION *SQL_updates = NULL;
unsigned int numUpdates = 0;
unsigned int numUpdatesAllocated = 0;

SQL_TRANSACTION *SQL_deletes = NULL;
unsigned int numDeletes = 0;
unsigned int numDeletesAllocated = 0;
....


Each SQL_TRANSACTION type has a function like this:

/* Add *string to the inserts array */
void SQL_Addto_Inserts(SQL_TRANSACTION *trans) {
  if (numInserts == numInsertsAllocated) {
    /* Increase the allocations */
    numInsertsAllocated += GROWSIZE;
  
    /* I know the return pointer shouldn't be the same pointer passed to realloc, but for now this works and will do! */
    SQL_inserts = (SQL_TRANSACTION*) realloc(SQL_inserts, (SQL_MAX_QUERY_SIZE * numInsertsAllocated));
    if (SQL_inserts == NULL) {
      printf("realloc failed\n");
      exit(1);
    }
  }
  
  /* More code to format the trans->string and 
  memcpy it to SQL_inserts[numInserts] */
  
  ++numInserts;
}



Everything works well like it is, but I want to replace the three functions with one generic function, but I can't work out how to point to the correct array to work with. I have something similar to this:

....
/* Transaction types */
#define SQL_UPDATE 1
#define SQL_INSERT 2
#define SQL_DELETE 3
....
/* transType is one of the above transaction types, passed by the calling function */
void SQL_Addto_Array(unsigned int transType, SQL_TRANSACTION *trans) {
  SQL_TRANSACTION **array = NULL; /* Is it correct to use the double ** notation for what I'm trying to do? */
  unsigned int *num = NULL;
  unsigned int *allocated = NULL;

  switch (transType) {
    case SQL_UPDATE:
      /* Point the pointers to the correct places */
      num = &numInserts;
      allocated = &numInsertsAllocated;
      array = &SQL_inserts; /* Is this incorrect? */
      break;
    case SQL_INSERT:
    ....
    /* Code for the other types */
    ....
  } /* End switch */
  
  if (*num == *allocated) {
    *allocated += SQL_GROWSIZE;
    
    array = (SQL_TRANSACTION*) realloc(array, (SQL_MAX_QUERY_SIZE * (*allocated))); /* I'm fairly certain this is wrong too! */
    if (array == NULL) {
      printf("realloc failed\n");
      exit(1);
    }
  }

  /* More code to format the trans->string */

  /* How do I memcpy trans->string now? */
  memcpy(array[*num], trans->string, strlen(trans->string)+1); /* Will probably explode the program! (The +1 is to copy the \0 byte) */
    
  ++(*num);
}


The above attempt crashes with... *** glibc detected *** ./wiimpdb.x86: realloc(): invalid pointer: 0x080f29f0 *** ... and anything else I've tried has similar results. I don't really understand the use of pointers to pointers, and reading the docs/tutorials I've found online has just confused me more. Whats the correct way to point the array pointer to where it needs to point, and how do I realloc the pointer it points to? *head explodes* [dead] [Edited by - DrTwox on August 16, 2008 12:05:47 AM]
Advertisement
Hey, I'm not sure I have a solution to your problem, but I looked over your code and suggest you look at how you use pointers..

why are 'num' and 'allocated' pointers? Everytime you use them you have to reference (&) them, and I can't see anywhere you actually use their pointed value.

like

unsigned int *num = NULL;
unsigned int *allocated = NULL;

could just be

unsigned int num = 0;
unsigned int allocated = 0;

and then you wouldn't have to reference/dereference them all the time.

e.g.
if (*num == *allocated)
becomes
if( num == allocated)

this should make things simpler at least..

i think the line where you typecast the result of realloc(..) is the problem. Your forcing the result to become a pointer - to a pointer, when it really isn't - because you've declared it as array**. Try declaring 'array' as just a single pointer.

Also SQL_TRANSACTION is already a pointer to a struct, so in your function declaration

SQL_Addto_Array(unsigned int transType, SQL_TRANSACTION *trans)

it could just be

SQL_Addto_Array(unsigned int transType, SQL_TRANSACTION trans)

hope this helps..
Regarding the realloc:

// the originalarray = (SQL_TRANSACTION*) realloc(array, (SQL_MAX_QUERY_SIZE * (*allocated))); /* I'm fairly certain this is wrong too! */// this should work now*array = (SQL_TRANSACTION*) realloc(*array, (SQL_MAX_QUERY_SIZE * (*allocated)));


i.e. reallocate where the "array" actually points to
Quote:Original post by supamike
why are 'num' and 'allocated' pointers? Everytime you use them you have to reference (&) them, and I can't see anywhere you actually use their pointed value.

In the (switch) statement:
num = &numInserts
allocated = &numInsertsAllocated

Depending on the transType value they could also point to numUpdates and numUpdatesAllocated or numDeletes and numDeletesAllocated. If I want to add another transaction array, they would point the appropriate values for it too.
Quote:
i think the line where you typecast the result of realloc(..) is the problem. Your forcing the result to become a pointer - to a pointer, when it really isn't - because you've declared it as array**. Try declaring 'array' as just a single pointer.

In the working code with the three separate functions for each array, SQL_inserts, SQL_updates or SQL_deletes, the realloc call "grows" each array as needed, and it works well (tested up to about 8500 elements each). If I want to replace those three functions with one more generic function, how do I still keep the three (or more in the future) SQL_ arrays separated? If I just use that one single pointer, I wouldn't have three arrays anymore would I? Which is why I thought I needed the pointer to a pointer. Feel free to kick me for each time I'm wrong :)
Quote:Also SQL_TRANSACTION is already a pointer to a struct, so in your function declaration
SQL_Addto_Array(unsigned int transType, SQL_TRANSACTION *trans)
it could just be
SQL_Addto_Array(unsigned int transType, SQL_TRANSACTION trans)
hope this helps..

Cool, thanks!

Quote:Original post by plastique
// this should work now
*array = (SQL_TRANSACTION*) realloc(*array, (SQL_MAX_QUERY_SIZE * (*allocated)));

Yes, it does work! (By work I mean compile without complaint and not crash on that line anymore :) ) How do I memcpy into the array as well?
memcpy(array[*num], trans->string, sizeof(trans->string)+1); seems to work for the first call to the function, then segfaults on the second.
I was mainly commenting on the realloc. But anyway, I just went through the whole code now and i can confirm (as the other guy already mentioned) that you need to also allocate extra memory for the strings, so basically

- allocate an array of SQL_TRANSACTION, i.e. x times sizeof(SQL_TRANSACTION), x seems to be "numInsertsAllocated" here
- for each (new) x, allocate an array of y count unsigned chars (y seems to be SQL_MAX_QUERY_SIZE here)

then you can do the memory copy like e.g.
memcpy((*array)[*num].string, trans->string, SQL_MAX_QUERY_SIZE);
Thanks to both of you for you suggestions and help. I've got something that works now. I think the code is long overdue for a cleanup! Hopefully I wont break anything. [smile]

This topic is closed to new replies.

Advertisement