tags:

views:

376

answers:

4

I'm trying to cast a struct into another struct but I'm having incompatible pointer issues within the cast and the malloc under some_func (structs layout are the same)

struct stu1 **some_func(struct stu1 *my_struct)
{
   my_struct = (struct stu1 **)malloc(sizeof(struct stu1 *)*total_size);

   for(i=0;i<20;i++){
      my_struct[i] = (struct stu1 *)malloc(sizeof(struct stu1));
      printf("%s",my_struct[i++]->a);
   }
   return my_struct;
}

int main() 
{
   struct stu1 **my_struct;
   struct stu2 **my_struct2;
   struct stu3 **my_struct3;

   my_struct = some_func(my_struct);
   my_struct2 = (struct stu2**)some_func((struct stu1*)my_struct2);
   my_struct3 = (struct stu3**)some_func((struct stu1*)my_struct3);    
}
+1  A: 
struct stu1 **some_func(struct stu1 *my_struct)
{
   my_struct = (struct stu1 **)malloc(sizeof(struct stu1 *)*total_size);

my_struct is of type struct stu1 *, but you are trying to assign a (casted) struct stu1 **

David Rodríguez - dribeas
A: 
Henrik
A: 

Where to begin ...

  1. You must match your types. some_func() expects a single pointer; in one instance you are passing a double pointer (a pointer to a pointer).
  2. Inside some_func(), the 1st malloc() has a type-mismatch. It is attempting to set a double pointer to a single pointer.
  3. Inside some_func(), the 2nd malloc() is has another type-mismatch. It is attempting to set a non-pointer (the structure) to a pointer.

Inside some_func(), it appears as though you may be trying to dynamically a two-dimensional array. For reference, there are two ways to do this with malloc().

Method #1. (Without error checking)

struct stu1 **some_func(int dim1, int dim2)
{
   struct stu1 **my_struct;
   int  i;

   /* Allocate space for an array of <dim1> pointers */
   my_struct = (struct stu1 **) malloc(sizeof(struct stu1 *) * dim1);

   /* Allocate <dim2> elements for each pointer */
   for (i = 0; i < dim1; i++){
      my_struct[i] = (struct stu1 *) malloc (sizeof (struct stu1) * dim2);
   }
   return (my_struct);
}

Method #2. (Again, without error checking)

struct stu1 **some_func(int dim1, int dim2)
{
   struct stu1 **my_struct;
   int  i;
   char *data;

   /* Allocate space for everything. */
   data = (char *) malloc((sizeof (struct stu1 *) * dim1) + (dim1 * dim2 * sizeof (struct stu1)));
   my_struct = (struct stu1 **) data;

   /* Initialize pointers to pointers. */
   data = (char *) &my_struct[dim1];
   for (i = 0; i < dim1; i++) {
      my_struct[i] = (struct stu1 *) data;
      data += sizeof (struct stu1) * dim2;
   }
   return (my_struct);
}

Each method has its advantages and disadvantages. Hope this helps.

Sparky
+2  A: 

Several problems.

Firstly, you have incompatible types all over the place. In the line

my_struct = some_func(my_struct);

my_struct has type struct stu1 **, but the definition of some_func expects a parameter of type struct stu1 *; the two types are not the same. A pointer to T is not the same type as a pointer to pointer to T.

Secondly, your casting gymnastics are not going to work as you expect. Pointer types are not automatically compatible; their base types must be compatible, and different struct types are not compatible, even if their layouts are the same, e.g.:

struct {int x, int y} foo;
struct {int x, int y} bar;
struct S {int x, int y} blurga;
struct S bletch;

foo, bar, and bletch are of different types and are not compatible, even though their layouts are the same. blurga and bletch are of the same type (struct S). If struct stu2 or struct stu3 have different sizes from struct stu1, then you won't allocate the right amount of memory for my_struct2 and my_struct3.

For the sake of clarity and your sanity, you should have different allocation functions for each type, rather than trying to force a square peg into a pentagonal hole:

struct stu1 **stu1_alloc(size_t count)
{
  struct stu1 **arr = malloc(sizeof *arr * count);
  if (arr)
  {
    size_t i;
    for (i = 0; i < count; i++)
    {
      arr[i] = malloc(sizeof *arr[i]);
      if (arr[i])
      {
        // initialize arr[i] as necessary
      }
    }
  }
  return arr;
}

struct stu2 **stu2_alloc(size_t count)
{
  struct stu2 **arr = malloc(sizeof *arr * count);
  if (arr)
  {
    size_t i;
    for (i = 0; i < count; i++)
    {
      arr[i] = malloc(sizeof *arr[i]);
      if (arr[i])
      {
        // initialize arr[i] as necessary
      }
    }
  }
  return arr;
}

struct stu3 **stu3_alloc(size_t count)
{
  struct stu3 **arr = malloc(sizeof *arr * count);
  if (arr)
  {
    size_t i;
    for (i = 0; i < count; i++)
    {
      arr[i] = malloc(sizeof *arr[i]);
      if (arr[i])
      {
        // initialize arr[i] as necessary
      }
    }
  }
  return arr;
}

int main(void)
{
  struct stu1 **my_struct = stu1_alloc(SIZE);
  struct stu2 **my_struct2 = stu2_alloc(SIZE2);
  struct stu3 **my_struct3 = stu3_alloc(SIZE3);
  ...
}

Yes, the only thing different between the three allocation functions is the type. However, if the different struct types have different sizes or different initialization needs, then this is necessary.

Notice a couple of things. First of all, I'm not casting the result of malloc(). There are two reasons for this. One, as of C89 and later, you don't have to: malloc() returns a type of void *, which is implicitly converted to the target pointer type. Two, and more importantly, if I forget to include stdlib.h or otherwise don't have a prototype for malloc() in scope, leaving the cast off will trigger an "incompatible types" warning (since undeclared functions are assumed to return int, and you cannot implicitly convert an int value to a pointer type). If you cast the return value of malloc(), then you supress that warning and risk problems at runtime (since the value returned by malloc() will be converted from void * to int, and then from int to the target pointer type, which is not guaranteed to work).

Secondly, I'm using sizeof on the object, not the type. This has two benefits. One, the code is a little easier to read. Two, if I change the type of the object, I don't have to go back and change every call to malloc().

If you really don't want to have three separate allocation functions, you could try some macro magic like this:

#define ALLOC_STU(target, size)                \
  do {                                         \
    target = malloc(sizeof *target * size);    \
    if (target)                                \
    {                                          \
      size_t i;                                \
      for (i = 0; i < size; i++)               \
      {                                        \
        target[i] = malloc(sizeof *target[i]); \
      }                                        \
    }                                          \
  } while(0)

int main(void)
{
  struct stu1 **my_struct;
  ...
  ALLOC_STU(my_struct, SIZE);
  ...
}

although I think the separate allocation functions are the safer way to go.

John Bode