I tried to implement a generic stack in C using `void` pointers and tried to keep it as simple as possible by delegating all responsibility to the caller of the functions and avoiding more sophisticated approaches.

**stack.h**

    #ifndef STACK_H
    #define STACK_H

    #include <stdbool.h>

    struct Stack {
    	void *data;
    	struct Stack *next;
    };

    /*
     * We declare a pointer to a Stack structure thereby making use of incomplete
     * types. Clients that pull in stack.h will be able to declare variables of type
     * pstack which are pointers to pointers to Stack structures.
     * */
    typedef struct Stack *pstack;

    bool is_empty(pstack *s);
    void make_empty(pstack *s);
    void push(struct Stack **s, void *new_num);
    void *pop(pstack *s);

    #endif /* STACK_H */

**stack.c**

    #include <stdio.h>
    #include <stdlib.h>
    #include "stack.h"

    bool is_empty(pstack *s) { return !s; }

    void make_empty(pstack *s)
    {
    	if (!is_empty(s))
    		pop(s);
    }

    void *pop(pstack *s)
    {
    	struct Stack *tmp;
    	void *i;

    	if (is_empty(s))
    		exit(EXIT_FAILURE);

    	tmp = *s;
    	i = (*s)->data;
    	*s = (*s)->next;
    	free(tmp);
    	return i;
    }

    void push(struct Stack **s, void *new_num)
    {
    	struct Stack *new_node = malloc(sizeof(struct Stack));
    	if (!new_node)
    		exit(EXIT_FAILURE);

    	new_node->data = new_num;
    	new_node->next = *s;
    	*s = new_node;
    }

**stackclient.c**

    #include <stdio.h>
    #include <stdlib.h>
    #include "stack.h"

    int main(void)
    {
    	pstack s1;
    	void *n;
    	int i = 1;
    	int j = 2;

    	push(&s1, &i);
    	push(&s1, &j);

    	n = pop(&s1);
    	printf("Popped %d from s1\n", *((int *)n));
    	n = pop(&s1);
    	printf("Popped %d from s1\n", *((int *)n));

    	exit(EXIT_SUCCESS);
    }