Typedef Struct (Void Elems Int Elemsize Int Loglength Int Alloclength ) Stack
Typedef Struct (Void Elems Int Elemsize Int Loglength Int Alloclength ) Stack
h
typedef struct {
void *elems;
int elemSize;
int logLength;
int allocLength;
} stack;
stack.h Writing a generic container in pure C is hard, and
it’s hard for two reasons:
typedef struct {
void *elems; 4. The language doesn’t offer any real support for encapsulation or
int elemSize; information hiding. That means that the data structures expose
int logLength;
information about internal representation right there in the
int allocLength;
interface file for everyone to see and manipulate. The best we can
} stack;
do is document that the data structure should be treated as an
abstract data type, and that the client shouldn’t directly manage the
fields. Instead, he should just rely on the functions provided to
manage the internals for him.
5. C doesn’t allow data types to be passed as parameters. That means
a generic container needs to manually manage memory in terms of
the client element size, not client data type. This translates to a
bunch of malloc, realloc, free, memcpy, and memmove calls
involving void *s. This is the very type of programming that makes
C difficult.
stack.h
typedef struct {
void *elems;
int elemSize;
int logLength;
int allocLength;
} stack;
void StackNew(stack *s, int elemSize);
void StackDispose(stack *s);
bool StackEmpty(const stack *s);
void StackPush(stack *s, const void *elemAddr);
void StackPop(stack *s, void *elemAddr);
This is what you see as a client.
stack.h
typedef struct {
void *elems;
int elemSize;
int logLength;
int allocLength;
} stack;
void StackNew(stack *s, int elemSize);
void StackDispose(stack *s);
bool StackEmpty(const stack *s);
void StackPush(stack *s, const void *elemAddr);
void StackPop(stack *s, void *elemAddr);
This is what you see as a client, but as a client, you should
ignore the internals of an exposed data structure unless the
documentation explicitly says otherwise.
stack.h
typedef struct {
void *elems;
int elemSize;
int logLength;
int allocLength;
} stack;
void StackNew(stack *s, int elemSize);
void StackDispose(stack *s);
bool StackEmpty(const stack *s);
void StackPush(stack *s, const void *elemAddr);
void StackPop(stack *s, void *elemAddr);
client.c
int main(int argc, char *argv[])
{
int val;
stack intStack;
StackNew(&intStack, sizeof(int));
for (val = 0; val < 6; val++)
StackPush(&intStack, &val);
while (!StackEmpty(&intStack)) {
StackPop(&intStack, &val);
printf("This just popped: %d\n", val);
}
StackDispose(&intStack);
}
client.c
int main(int argc, char *argv[])
{
int val;
stack intStack;
StackNew(&intStack, sizeof(int));
for (val = 0; val < 6; val++)
StackPush(&intStack, &val);
while (!StackEmpty(&intStack)) {
StackPop(&intStack, &val);
printf("This just popped: %d\n", val);
}
StackDispose(&intStack);
}
client.c
int main(int argc, char *argv[])
{
int val;
stack intStack;
StackNew(&intStack, sizeof(int));
for (val = 0; val < 6; val++)
StackPush(&intStack, &val);
while (!StackEmpty(&intStack)) {
StackPop(&intStack, &val);
printf("This just popped: %d\n", val);
}
StackDispose(&intStack);
}
val
client.c
int main(int argc, char *argv[])
{
int val;
stack intStack;
StackNew(&intStack, sizeof(int));
for (val = 0; val < 6; val++)
StackPush(&intStack, &val);
while (!StackEmpty(&intStack)) {
StackPop(&intStack, &val);
printf("This just popped: %d\n", val);
}
StackDispose(&intStack);
}
val
intStack
client.c
int main(int argc, char *argv[])
{
int val;
stack intStack;
StackNew(&intStack, sizeof(int));
for (val = 0; val < 6; val++)
StackPush(&intStack, &val);
while (!StackEmpty(&intStack)) {
StackPop(&intStack, &val);
printf("This just popped: %d\n", val);
}
StackDispose(&intStack);
}
val Obviously the stack is being updated, but the drawings come in pale,
muted colors to emphasize the fact that clients shouldn’t really know or
4 care about a stack’s internal state.
0
sizeof(int)
intStack
client.c
int main(int argc, char *argv[])
{
int val;
stack intStack;
StackNew(&intStack, sizeof(int));
for (val = 0; val < 6; val++)
StackPush(&intStack, &val);
while (!StackEmpty(&intStack)) {
StackPop(&intStack, &val);
printf("This just popped: %d\n", val);
}
StackDispose(&intStack);
}
val 0
sizeof(int)
intStack
client.c
int main(int argc, char *argv[])
{
int val;
stack intStack;
StackNew(&intStack, sizeof(int));
for (val = 0; val < 6; val++)
StackPush(&intStack, &val);
while (!StackEmpty(&intStack)) {
StackPop(&intStack, &val);
printf("This just popped: %d\n", val);
}
StackDispose(&intStack);
}
val 0
sizeof(int)
intStack 00 00 00 00
client.c
int main(int argc, char *argv[])
{
int val;
stack intStack;
StackNew(&intStack, sizeof(int));
for (val = 0; val < 6; val++)
StackPush(&intStack, &val);
while (!StackEmpty(&intStack)) {
StackPop(&intStack, &val);
printf("This just popped: %d\n", val);
}
StackDispose(&intStack);
}
val 1
sizeof(int)
intStack 00 00 00 00
client.c
int main(int argc, char *argv[])
{
int val;
stack intStack;
StackNew(&intStack, sizeof(int));
for (val = 0; val < 6; val++)
StackPush(&intStack, &val);
while (!StackEmpty(&intStack)) {
StackPop(&intStack, &val);
printf("This just popped: %d\n", val);
}
StackDispose(&intStack);
}
val 1
sizeof(int)
client.c
int main(int argc, char *argv[])
{
int val;
stack intStack;
StackNew(&intStack, sizeof(int));
for (val = 0; val < 6; val++)
StackPush(&intStack, &val);
while (!StackEmpty(&intStack)) {
StackPop(&intStack, &val);
printf("This just popped: %d\n", val);
}
StackDispose(&intStack);
}
val 2
sizeof(int)
client.c
int main(int argc, char *argv[])
{
int val;
stack intStack;
StackNew(&intStack, sizeof(int));
for (val = 0; val < 6; val++)
StackPush(&intStack, &val);
while (!StackEmpty(&intStack)) {
StackPop(&intStack, &val);
printf("This just popped: %d\n", val);
}
StackDispose(&intStack);
}
val 2
sizeof(int)
client.c
int main(int argc, char *argv[])
{
int val;
stack intStack;
StackNew(&intStack, sizeof(int));
for (val = 0; val < 6; val++)
StackPush(&intStack, &val);
while (!StackEmpty(&intStack)) {
StackPop(&intStack, &val);
printf("This just popped: %d\n", val);
}
StackDispose(&intStack);
}
val 3
sizeof(int)
client.c
int main(int argc, char *argv[])
{
int val;
stack intStack;
StackNew(&intStack, sizeof(int));
for (val = 0; val < 6; val++)
StackPush(&intStack, &val);
while (!StackEmpty(&intStack)) {
StackPop(&intStack, &val);
printf("This just popped: %d\n", val);
}
StackDispose(&intStack);
}
val 3
sizeof(int)
client.c
int main(int argc, char *argv[])
{
int val;
stack intStack;
StackNew(&intStack, sizeof(int));
for (val = 0; val < 6; val++)
StackPush(&intStack, &val);
while (!StackEmpty(&intStack)) {
StackPop(&intStack, &val);
printf("This just popped: %d\n", val);
}
StackDispose(&intStack);
}
val 4
sizeof(int)
client.c
int main(int argc, char *argv[])
{
int val;
stack intStack;
StackNew(&intStack, sizeof(int));
for (val = 0; val < 6; val++)
StackPush(&intStack, &val);
while (!StackEmpty(&intStack)) {
StackPop(&intStack, &val);
printf("This just popped: %d\n", val);
}
StackDispose(&intStack);
}
val 4
sizeof(int)
client.c
int main(int argc, char *argv[])
{
int val;
stack intStack;
StackNew(&intStack, sizeof(int));
for (val = 0; val < 6; val++)
StackPush(&intStack, &val);
while (!StackEmpty(&intStack)) {
StackPop(&intStack, &val);
printf("This just popped: %d\n", val);
}
StackDispose(&intStack);
}
val 5
sizeof(int)
client.c
int main(int argc, char *argv[])
{
int val;
stack intStack;
StackNew(&intStack, sizeof(int));
for (val = 0; val < 6; val++)
StackPush(&intStack, &val);
while (!StackEmpty(&intStack)) {
StackPop(&intStack, &val);
printf("This just popped: %d\n", val);
}
StackDispose(&intStack);
}
val 5
sizeof(int)
client.c
int main(int argc, char *argv[])
{
int val;
stack intStack;
StackNew(&intStack, sizeof(int));
for (val = 0; val < 6; val++)
StackPush(&intStack, &val);
while (!StackEmpty(&intStack)) {
StackPop(&intStack, &val);
printf("This just popped: %d\n", val);
}
StackDispose(&intStack);
}
val 6
sizeof(int)
client.c
int main(int argc, char *argv[])
{
int val;
stack intStack;
StackNew(&intStack, sizeof(int));
for (val = 0; val < 6; val++)
StackPush(&intStack, &val);
while (!StackEmpty(&intStack)) {
StackPop(&intStack, &val);
printf("This just popped: %d\n", val);
}
StackDispose(&intStack);
}
val 6
sizeof(int)
client.c
int main(int argc, char *argv[])
{
int val;
stack intStack;
StackNew(&intStack, sizeof(int));
for (val = 0; val < 6; val++)
StackPush(&intStack, &val);
while (!StackEmpty(&intStack)) {
StackPop(&intStack, &val);
printf("This just popped: %d\n", val);
}
StackDispose(&intStack);
}
val 5
sizeof(int)
client.c
Output
int main(int argc, char *argv[])
{ This just popped: 5
int val;
stack intStack;
StackNew(&intStack, sizeof(int));
for (val = 0; val < 6; val++)
StackPush(&intStack, &val);
while (!StackEmpty(&intStack)) {
StackPop(&intStack, &val);
printf("This just popped: %d\n", val);
}
StackDispose(&intStack);
}
val 5
sizeof(int)
client.c
Output
int main(int argc, char *argv[])
{ This just popped: 5
int val;
stack intStack;
StackNew(&intStack, sizeof(int));
for (val = 0; val < 6; val++)
StackPush(&intStack, &val);
while (!StackEmpty(&intStack)) {
StackPop(&intStack, &val);
printf("This just popped: %d\n", val);
}
StackDispose(&intStack);
}
val 5
sizeof(int)
client.c
Output
int main(int argc, char *argv[])
{ This just popped: 5
int val;
stack intStack;
StackNew(&intStack, sizeof(int));
for (val = 0; val < 6; val++)
StackPush(&intStack, &val);
while (!StackEmpty(&intStack)) {
StackPop(&intStack, &val);
printf("This just popped: %d\n", val);
}
StackDispose(&intStack);
}
val 4
sizeof(int)
client.c
Output
int main(int argc, char *argv[])
{ This just popped: 5
int val; This just popped: 4
stack intStack;
StackNew(&intStack, sizeof(int));
for (val = 0; val < 6; val++)
StackPush(&intStack, &val);
while (!StackEmpty(&intStack)) {
StackPop(&intStack, &val);
printf("This just popped: %d\n", val);
}
StackDispose(&intStack);
}
val 4
sizeof(int)
client.c
Output
int main(int argc, char *argv[])
{ This just popped: 5
int val; This just popped: 4
stack intStack;
StackNew(&intStack, sizeof(int));
for (val = 0; val < 6; val++)
StackPush(&intStack, &val);
while (!StackEmpty(&intStack)) {
StackPop(&intStack, &val);
printf("This just popped: %d\n", val);
}
StackDispose(&intStack);
}
val 4
sizeof(int)
client.c
Output
int main(int argc, char *argv[])
{ This just popped: 5
int val; This just popped: 4
stack intStack;
StackNew(&intStack, sizeof(int));
for (val = 0; val < 6; val++)
StackPush(&intStack, &val);
while (!StackEmpty(&intStack)) {
StackPop(&intStack, &val);
printf("This just popped: %d\n", val);
}
StackDispose(&intStack);
}
val 3
sizeof(int)
client.c
Output
int main(int argc, char *argv[])
{ This just popped: 5
int val; This just popped: 4
stack intStack; This just popped: 3
StackNew(&intStack, sizeof(int));
for (val = 0; val < 6; val++)
StackPush(&intStack, &val);
while (!StackEmpty(&intStack)) {
StackPop(&intStack, &val);
printf("This just popped: %d\n", val);
}
StackDispose(&intStack);
}
val 3
sizeof(int)
client.c
Output
int main(int argc, char *argv[])
{ This just popped: 5
int val; This just popped: 4
stack intStack; This just popped: 3
StackNew(&intStack, sizeof(int));
for (val = 0; val < 6; val++)
StackPush(&intStack, &val);
while (!StackEmpty(&intStack)) {
StackPop(&intStack, &val);
printf("This just popped: %d\n", val);
}
StackDispose(&intStack);
}
val 3
sizeof(int)
client.c
Output
int main(int argc, char *argv[])
{ This just popped: 5
int val; This just popped: 4
stack intStack; This just popped: 3
StackNew(&intStack, sizeof(int));
for (val = 0; val < 6; val++)
StackPush(&intStack, &val);
while (!StackEmpty(&intStack)) {
StackPop(&intStack, &val);
printf("This just popped: %d\n", val);
}
StackDispose(&intStack);
}
val 2
sizeof(int)
client.c
Output
int main(int argc, char *argv[])
{ This just popped: 5
int val; This just popped: 4
stack intStack; This just popped: 3
This just popped: 2
StackNew(&intStack, sizeof(int));
for (val = 0; val < 6; val++)
StackPush(&intStack, &val);
while (!StackEmpty(&intStack)) {
StackPop(&intStack, &val);
printf("This just popped: %d\n", val);
}
StackDispose(&intStack);
}
val 2
sizeof(int)
client.c
Output
int main(int argc, char *argv[])
{ This just popped: 5
int val; This just popped: 4
stack intStack; This just popped: 3
This just popped: 2
StackNew(&intStack, sizeof(int));
for (val = 0; val < 6; val++)
StackPush(&intStack, &val);
while (!StackEmpty(&intStack)) {
StackPop(&intStack, &val);
printf("This just popped: %d\n", val);
}
StackDispose(&intStack);
}
val 2
sizeof(int)
client.c
Output
int main(int argc, char *argv[])
{ This just popped: 5
int val; This just popped: 4
stack intStack; This just popped: 3
This just popped: 2
StackNew(&intStack, sizeof(int));
for (val = 0; val < 6; val++)
StackPush(&intStack, &val);
while (!StackEmpty(&intStack)) {
StackPop(&intStack, &val);
printf("This just popped: %d\n", val);
}
StackDispose(&intStack);
}
val 1
sizeof(int)
client.c
Output
int main(int argc, char *argv[])
{ This just popped: 5
int val; This just popped: 4
stack intStack; This just popped: 3
This just popped: 2
StackNew(&intStack, sizeof(int)); This just popped: 1
for (val = 0; val < 6; val++)
StackPush(&intStack, &val);
while (!StackEmpty(&intStack)) {
StackPop(&intStack, &val);
printf("This just popped: %d\n", val);
}
StackDispose(&intStack);
}
val 1
sizeof(int)
client.c
Output
int main(int argc, char *argv[])
{ This just popped: 5
int val; This just popped: 4
stack intStack; This just popped: 3
This just popped: 2
StackNew(&intStack, sizeof(int)); This just popped: 1
for (val = 0; val < 6; val++)
StackPush(&intStack, &val);
while (!StackEmpty(&intStack)) {
StackPop(&intStack, &val);
printf("This just popped: %d\n", val);
}
StackDispose(&intStack);
}
val 1
sizeof(int)
client.c
Output
int main(int argc, char *argv[])
{ This just popped: 5
int val; This just popped: 4
stack intStack; This just popped: 3
This just popped: 2
StackNew(&intStack, sizeof(int)); This just popped: 1
for (val = 0; val < 6; val++)
StackPush(&intStack, &val);
while (!StackEmpty(&intStack)) {
StackPop(&intStack, &val);
printf("This just popped: %d\n", val);
}
StackDispose(&intStack);
}
val 0
sizeof(int)
client.c
Output
int main(int argc, char *argv[])
{ This just popped: 5
int val; This just popped: 4
stack intStack; This just popped: 3
This just popped: 2
StackNew(&intStack, sizeof(int)); This just popped: 1
for (val = 0; val < 6; val++) This just popped: 0
StackPush(&intStack, &val);
while (!StackEmpty(&intStack)) {
StackPop(&intStack, &val);
printf("This just popped: %d\n", val);
}
StackDispose(&intStack);
}
val 0
sizeof(int)
client.c
Output
int main(int argc, char *argv[])
{ This just popped: 5
int val; This just popped: 4
stack intStack; This just popped: 3
This just popped: 2
StackNew(&intStack, sizeof(int)); This just popped: 1
for (val = 0; val < 6; val++) This just popped: 0
StackPush(&intStack, &val);
while (!StackEmpty(&intStack)) {
StackPop(&intStack, &val);
printf("This just popped: %d\n", val);
}
StackDispose(&intStack);
}
val 0
sizeof(int)
client.c
Output
int main(int argc, char *argv[])
{ This just popped: 5
int val; This just popped: 4
stack intStack; This just popped: 3
This just popped: 2
StackNew(&intStack, sizeof(int)); This just popped: 1
for (val = 0; val < 6; val++) This just popped: 0
StackPush(&intStack, &val);
while (!StackEmpty(&intStack)) {
StackPop(&intStack, &val);
printf("This just popped: %d\n", val);
}
StackDispose(&intStack);
}
val 0
sizeof(int)
intStack
client.c
Output
int main(int argc, char *argv[])
{ This just popped: 5
int val; This just popped: 4
stack intStack; This just popped: 3
This just popped: 2
StackNew(&intStack, sizeof(int)); This just popped: 1
for (val = 0; val < 6; val++) This just popped: 0
StackPush(&intStack, &val);
while (!StackEmpty(&intStack)) {
StackPop(&intStack, &val);
printf("This just popped: %d\n", val);
}
StackDispose(&intStack);
}
stack.h
typedef struct {
void *elems;
int elemSize;
int logLength;
int allocLength;
} stack;
void StackNew(stack *s, int elemSize);
void StackDispose(stack *s);
bool StackEmpty(const stack *s);
void StackPush(stack *s, const void *elemAddr);
void StackPop(stack *s, void *elemAddr);
stack.c
We’re about to implement the five functions. Keep in mind that the implementation is generic, and never
has the full story about what it’s storing. It’s only because the client passes the base element size to the
constructor that it’s capable of cloning client data behind the scenes.
Here comes an implementation that works for any primitive type.
stack.h
typedef struct {
void *elems;
int elemSize;
int logLength;
int allocLength;
} stack;
void StackNew(stack *s, int elemSize);
void StackDispose(stack *s);
bool StackEmpty(const stack *s);
void StackPush(stack *s, const void *elemAddr);
void StackPop(stack *s, void *elemAddr);
stack.c
#define kInitialAllocationSize 4
void StackNew(stack *s, int elemSize)
{
assert(elemSize > 0);
s>elemSize = elemSize;
s>logLength = 0;
s>allocLength = kInitialAllocationSize;
s>elems = malloc(kInitialAllocationSize * elemSize);
assert(s>elems != NULL);
}
stack.h
typedef struct {
void *elems;
int elemSize;
int logLength;
int allocLength; Key observations:
} stack; • The constructor requires that the
client element size be identified up
void StackNew(stack *s, int elemSize); front. Since it doesn’t (and will never)
void StackDispose(stack *s); know the true data type, it needs the
bool StackEmpty(const stack *s); size so it at least knows how many
void StackPush(stack *s, const void *elemAddr); bytes to replicate behind the scenes
void StackPop(stack *s, void *elemAddr); with every call to StackPush.
• The implementation chooses to
stack.c allocate space for 4 client elements.
The 4 is arbitrary, but it should
#define kInitialAllocationSize 4 probably be small, since a good
number of applications never deal with
void StackNew(stack *s, int elemSize) stacks that are all that deep. If during
{ the course of a program the stack
assert(elemSize > 0); saturates allocated memory, the
s>elemSize = elemSize; implementation can just reallocate the
s>logLength = 0; storage using a (popular) doubling
s>allocLength = kInitialAllocationSize; strategy. You’ll see.
s>elems = malloc(kInitialAllocationSize * elemSize);
assert(s>elems != NULL);
}
stack.h
typedef struct {
void *elems;
int elemSize;
int logLength;
int allocLength; Key observations:
} stack; • The destructor simply needs to dispose
of any resources that’ll otherwise be
void StackNew(stack *s, int elemSize); orphaned. The client has no clue that
void StackDispose(stack *s); one single block of dynamically
bool StackEmpty(const stack *s); allocated memory is being used to
void StackPush(stack *s, const void *elemAddr); house all of the elements, so the
void StackPop(stack *s, void *elemAddr); implementation needs to properly free
that memory, else it’ll be orphaned
stack.c and unavailable for the lifetime of the
application.
#define kInitialAllocationSize 4 • Note that we don’t call free(s).
StackNew doesn’t allocate space for
void StackDispose(stack *s) the stack struct—the client does! The
{ stack destructor should only clean up
free(s>elems); its own mess, not someone else’s.
}
stack.h
typedef struct {
void *elems;
int elemSize;
int logLength;
int allocLength; Key observations:
} stack; • The destructor simply needs to dispose
of any resources that’ll otherwise be
void StackNew(stack *s, int elemSize); orphaned. The client has no clue that
void StackDispose(stack *s); one single block of dynamically
bool StackEmpty(const stack *s); allocated memory is being used to
void StackPush(stack *s, const void *elemAddr); house all of the elements, so the
void StackPop(stack *s, void *elemAddr); implementation needs to properly free
that memory, else it’ll be orphaned
stack.c and unavailable for the lifetime of the
application.
#define kInitialAllocationSize 4 • Note that we don’t call free(s).
StackNew doesn’t allocate space for
void StackDispose(stack *s) the stack struct—the client does! The
{ stack destructor should only clean up
free(s>elems); its own mess, not someone else’s.
}
bool StackEmpty(const stack *s) • StackEmpty is a nobrainer.
{
return s>logLength == 0;
}
stack.h
typedef struct {
void *elems;
int elemSize;
int logLength;
int allocLength;
} stack;
void StackNew(stack *s, int elemSize);
void StackDispose(stack *s);
bool StackEmpty(const stack *s);
void StackPush(stack *s, const void *elemAddr);
void StackPop(stack *s, void *elemAddr);
stack.c
void StackPush(stack *s, const void *elemAddr)
{
void *destAddr;
if (s>logLength == s>allocLength) {
s>allocLength *= 2;
s>elems = realloc(s>elems, s>allocLength * s>elemSize);
assert(s>elems != NULL);
}
destAddr = (char *)s>elems + s>logLength * s>elemSize;
memcpy(destAddr, elemAddr, s>elemSize);
s>logLength++;
}
stack.h
typedef struct {
void *elems;
int elemSize;
int logLength;
int allocLength; The devil’s in the details:
} stack; • The last three lines replicate the byte
pattern addressed by elemAddr,
void StackNew(stack *s, int elemSize); placing it at the manually computed
void StackDispose(stack *s); address within raw storage.
bool StackEmpty(const stack *s); • Note that the copy is a shallow copy.
void StackPush(stack *s, const void *elemAddr); If the s>elemSize bytes copied in are
void StackPop(stack *s, void *elemAddr); pointers to dynamically allocated
memory, then ownership of that
stack.c memory is transferred from the client
to the implementation.
void StackPush(stack *s, const void *elemAddr)
{
void *destAddr;
if (s>logLength == s>allocLength) {
s>allocLength *= 2;
s>elems = realloc(s>elems, s>allocLength * s>elemSize);
assert(s>elems != NULL);
}
destAddr = (char *)s>elems + s>logLength * s>elemSize;
memcpy(destAddr, elemAddr, s>elemSize);
s>logLength++;
}
stack.h
typedef struct {
void *elems;
int elemSize;
int logLength;
int allocLength; The devil’s in the details:
} stack; • The last three lines replicate the byte
pattern addressed by elemAddr,
void StackNew(stack *s, int elemSize); placing it at the manually computed
void StackDispose(stack *s); address within raw storage.
bool StackEmpty(const stack *s); • Note that the copy is a shallow copy.
void StackPush(stack *s, const void *elemAddr); If the s>elemSize bytes copied in are
void StackPop(stack *s, void *elemAddr); pointers to dynamically allocated
memory, then ownership of that
stack.c memory is transferred from the client
to the implementation.
void StackPush(stack *s, const void *elemAddr) • The conditional reallocation only
{ applies if we’ve previously saturated
void *destAddr; memory. Type ‘man realloc’ at the
if (s>logLength == s>allocLength) { command line to get the full
s>allocLength *= 2; documentation on how realloc works.
s>elems = realloc(s>elems, s>allocLength * s>elemSize);
assert(s>elems != NULL);
}
destAddr = (char *)s>elems + s>logLength * s>elemSize;
memcpy(destAddr, elemAddr, s>elemSize);
s>logLength++;
}
stack.h
typedef struct {
void *elems;
int elemSize;
int logLength;
int allocLength; Final words on implementation
} stack; • StackPop is, not surprisingly, the reverse
of StackPush. It needs to transfer
void StackNew(stack *s, int elemSize); ownership of the byte pattern of the
void StackDispose(stack *s); topmost element back to the client, and it
bool StackEmpty(const stack *s); does so with a symmetric call to memcpy.
void StackPush(stack *s, const void *elemAddr); We manually compute the address of the
void StackPop(stack *s, void *elemAddr); byte pattern in precisely the same way,
and trust (how can we not?) that the
stack.c client has supplied the address of a figure
large enough to accommodate the byte
void StackPop(stack *s, void *elemAddr) transfer.
{ • We never reallocate the array to be
const void *sourceAddr; smaller, even if we dip below some 50%
saturation value. In practice, realloc
assert(!StackEmpty(s)); punts on any request to shrink the array
s>logLength; anyway, so it’s typically a wasted effort.
sourceAddr = (const char *) s>elems + s>logLength * s>elemSize;
memcpy(elemAddr, sourceAddr, s>elemSize);
}