Getting rid of the typedef may make things a little clearer, believe it or not:
struct StackRecord
{
int Capacity;
int TopOfStack;
int* Array;
};
/**
* Return a pointer to a new, dynamically allocated instance
* of struct StackRecord
*/
struct StackRecord *CreateStack(int MaxElements)
{
struct StackRecord *S;
if (MaxElements < MinStackSize)
{
printf("Error : Stack size is too small");
return 0;
}
S = malloc(sizeof *S); // no need for cast, sizeof *S is same as sizeof (struct StackRecord)
if (S == NULL)
{
printf("FatalError : Out of Space!!!");
return 0;
}
/**
* Allocate the memory for the Array member of
* the new stack record instance.
*/
S->Array = malloc( sizeof *S->Array * MaxElements );
if (S->Array == NULL)
{
printf("FatalError : Out of Space!!!");
return 0;
}
S->Capacity = MaxElements;
MakeEmpty(S);
return S;
}
In the code you posted, Stack
is basically a synonym for struct StackRecord *
. The function creates a new instance of struct StackRecord
using malloc
, initializes the contents of that record, and returns a pointer to that new instance.
A note on the malloc
calls - in C, you do not need to cast the result of malloc
, and doing so is generally considered bad practice1. Also, the operand to sizeof
doesn't have to be a type name - it can be an expression of the type you want to allocate. IOW, given a declaration like
T *p;
both sizeof (T)
and sizeof *p
do the same thing - the expression *p
has type T
. So the general form of a malloc call can be written as
T *p = malloc( sizeof *p * N );
or
T *p;
...
p = malloc( sizeof *p * N );
That's simpler to write and easier to maintain than
p = (T *) malloc( sizeof (T) * N );
<rant>
Hiding the pointer-ness of a type behind a typedef is bad juju, especially when the user of that type has to be aware that he or she is dealing with a pointer type. Assigning the result of malloc
to S
means that S
must be a pointer type. Using the ->
to access members of S
means that S
must be a pointer to a struct
or union
type. Since you have to be aware that S
is a pointer, it makes no sense to hide that pointerness behind the typedef. Similarly, if the user has to be aware of the struct
-ness of the type, you shouldn't hide that struct
-ness behind a typedef either.
Abstraction is a powerful tool, but partial (leaky) abstractions like the original code just make life more confusing for everyone (as you have discovered for yourself).
</rant>
- This is not true for C++, because C++ doesn't allow implicit conversions between
void *
and other pointer types the way C does. But, if you're writing C++, you shouldn't be using malloc
anyway.