0) For something like this, there is very little justification I could accept for "I have to use C" - except possibly "I'm doing this for school and my prof is stuck in the 80s". Although a prof stuck in the 80s wouldn't speak of ZeroMemory() but instead memset().
1) In particular, if you can't figure out these kinds of things for yourself, you have no business trying to do them in C. It's not as if there's any significant amount of fat *available* to trim out of a standard C++ (using the standard library) implementation, and there are much better ways to learn how low-level algorithmic ideas.
2) Notwithstanding that, and assuming we're stuck with C, there is much better code organization - and interface - available.
- In C, don't cast the result of malloc(). This hides the error of failing to include stdlib.h. Oh, make sure you include stdlib.h. :)
- "sizeof(void)" looks rather suspicious to me, especially as a malloc() argument. Just default to a NULL pointer.
- The ZeroMemory() call in the first source box is silly and redundant; you just finished initializing all the values, and then you scrub them all over anyway. BTW, ' ' does NOT have an ascii value of 0. It has an ascii value of 32.
- There's absolutely no reason to use a void** for the function prototype. While it's true that the StackNode.data member is of some unknown hidden-behind-void-* type, we're always going to pass in a StackNode to be initialized, and the list is always going to pop a StackNode out and assign from one StackNode to another.
- We could at least learn the lessons of C++ and set up free functions to do construction/destruction/assignment-like tasks, even if the language won't call them for us automatically. It's just good factoring.
- The interface is needlessly complex, and introduces a needless copy. Just have pop() *return* a StackNode*; return NULL if nothing is found, and otherwise return a pointer to the old top node *itself*, which has merely been disconnected. There's no reason to copy the data because *we're removing it from the list anyway*. Note that pop() as written leaks memory *twice*: once because OldHead is pointed at a new allocation and then re-pointed at the head of the list without anything being done with/to the new allocation, and again because the old head node is removed from the list and we lose the pointer to it at the end of the function, but never deallocate it. The proposed interface sidesteps that difficulty (except that the caller will have to deallocate).
- This assignment doesn't work for two reasons:
(*(StackNode*)(item)).data = *(int*)(oldHead->data);
First, the data from the old head is being dereferenced, to yield an int, but then the int is being directly assigned to the void* data member of 'item'. That doesn't match in terms of levels of indirection. Second, that 'data' member never actually got pointed towards any memory allocation, so there's nowhere to copy the int into. Note: void* is not "an untyped piece of memory", it is a pointer to such.
/* Our wrapper functions */StackNode* newStackNode() { StackNode* result = malloc(sizeof(StackNode)); if (!result) return NULL; result->data = NULL; result->next = NULL; result->prev = NULL; result->type = ' '; return result;}void destroyStackNode(StackNode* toDestroy) { switch(toDestroy->type) { case 'i': assert(toDestroy->data); free((int*)(toDestroy->data)); break; case ' ': break; default: assert(false); } free(toDestroy);}StackNode* intStackNode(int i) { StackNode* result = newStackNode(); if (!result) return NULL; result->data = malloc(sizeof(int)); if (!result->data) { destroyStackNode(result); return NULL; } result->type = 'i'; return result;}StackNode* copyStackNode(StackNode* other) { switch(other->type) { case 'i': /* The prev and next pointers are not "intrinsic" properties that we want to copy. */ return intStackNode(*(int*)(other->data)); case ' ': return newStackNode(); default: assert(false); }}void assignStackNode(StackNode* lhs, StackNode* rhs) { /* Behold, you can do copy-and-swap in C, although maybe you don't need to */ StackNode* other; void* tmp; other = copyStackNode(rhs); tmp = lhs.data; lhs.data = other.data; other.data = tmp; lhs.type = other.type; destroyStackNode(other);}/* We can access data like this: */int* getDataIfInt(StackNode* node) { return (node->type == 'i') ? (int*)(node->data) : NULL;}/* We'll call pop() like this: */StackNode *top;int* data;top = Pop();data = getDataIfInt();if (data) { printf("Contents were %d\n", *data);}destroyStackNode(top);/* You could wrap that kind of logic up into a function, too, if you don't want to burden clients with the destroyStackNode() responsibility. *//* We'll implement pop() like this: */StackNode* Pop() { StackNode* oldHead; oldHead = runTimeStack.head; if (!oldHead) { return NULL; } runTimeStack.head = runTimeStack.head->next; /* Disconnect stuff */ runTimeStack.head->prev = NULL; oldHead->next = NULL; /* Do upkeep on the stack */ runTimeStack.size--; return oldHead;}