Vaibhav Agarwal Vaibhav Agarwal - 1 year ago 62
C Question

Generic Stacks in C

I am implementing generic stack in C and I am facing a problem in stackPop method. My struct is as follows:

"Stack.h" file

typedef struct{
void *elems;
int elemSize;
int allocLength;
int logLength;
void (*freefnc)(void *);
} Stack;

void stackNew(Stack *s, int elemSize, void (*freefnc)(void *));
void stackDispose(Stack *s);
void stackPush(Stack *s, void *elemAddr);
void stackPop(Stack *s, void *target);


Stack.c

#inlcude<Stack.h>

void stackNew(Stack *s, int elemSize, void (*freefnc)(void *)){
s.allocLength = 4;
s.logLength = 0;
s.elemSize = elemSize;
s.elems = malloc(4*elemSize);
s.freefnc = freefnc;
}

void stackDispose(Stack *s){
if(s.freefnc!=NULL){
for(int i=0; i<s.logLength; i++){
freefnc((char *)s.elems+i*s->elemSize);
}
}
free(s.elems);
}

void Stringfree(void *elem){
free(*(char**)elem);
}

void stackPush(Stack *s, void *elemAddr){
if(s.alloclength == s.logLength){
stackGrow(s);
}
void *target = (char *)s.elems + s.logLength*s.elemSize;
memcpy(target,elemAddr,s.elemSize);
s.logLength++;
}

static void stackGrow(Stack *s){
s.allocLength*=2;
s.elems = realloc(s.elems, s.alloclength*s.elemSize);
assert(s.elems!=NULL);
}

void stackPop(Stack *s, void *elemAddr){
void *source = (char *)s.elems + (s.logLength-1)*s.elemSize;
memcpy(elemAddr,source,s.elemSize);
s.logLength--;
}


How do I return/get the value being popped off in stackPop function? I do not want to change the implementation but if there is a good way which keeps the implementation same to the extent as much as possible, then I would appreciate that. Other approaches are also acceptable.

Answer Source

I think the best you could do is to return a pointer to popped data (as a void* since that's about the best you can do in C for a 'generic' function):

void* stackPop(Stack *s, void *elemAddr){
        void *source = (char *)s.elems + (s.logLength-1)*s.elemSize;
        memcpy(elemAddr,source,s.elemSize);
        s.logLength--;
        return elemAddr;
}

Note that the caller still needs to provide the memory and the address to pop the data into; if you want ed you could avoid that by having the function malloc() the memory:

void* stackPop(Stack *s){
        void *source = (char *)s.elems + (s.logLength-1)*s.elemSize;
        void *elemAddr = malloc(s.elemSize);
        // if (!elemAddr) handle_error();
        memcpy(elemAddr,source,s.elemSize);
        s.logLength--;
        return elemAddr;
}

Of course that would require the caller to free() it when it is no longer needed, and adds the minor complication of needing to handle the out of memory situation.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download