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.