Basic 'char' Stack implementation in C #include &lt;stdio.h&gt;
#include &lt;assert.h&gt;
#include &lt;stdlib.h&gt;
#define STACK_SIZE 100

typedef struct _stack
{
    char *mem;
    size_t size;
    int top;
} STACK;

STACK *stack_create(char *mem, size_t size);
void stack_push(STACK *stack, char c);
char stack_pop(STACK *stack);
char stack_peek(STACK *const stack);
void stack_print(STACK *stack);
int main() {
    char stack_mem[STACK_SIZE];

    STACK *my_stack = stack_create(stack_mem, STACK_SIZE);
    stack_push(my_stack, 'a');
    stack_push(my_stack, 'b');
    stack_push(my_stack, 'c');
    stack_push(my_stack, 'd');
    stack_print(my_stack);
    printf("Top of stack contains: %c\n", stack_peek(my_stack));
    stack_pop(my_stack);
    stack_print(my_stack);
    stack_pop(my_stack);
    stack_pop(my_stack);
    stack_pop(my_stack);
    stack_push(my_stack, 'j');
    stack_push(my_stack, 'k');
    stack_print(my_stack);

    free(my_stack);
    return EXIT_SUCCESS;
}



STACK *stack_create(char *mem, size_t size) {

    assert(mem &amp;&amp; size);

    STACK *output = malloc(sizeof(STACK));

    output-&gt;mem = mem;
    output-&gt;size = size;
    output-&gt;top = -1;
    return output;
}

void stack_push(STACK *stack, char c)
{
    assert(stack);

    if(c &lt; 0)
    {
        fprintf(stderr, "This stack is not designed for negative char values\n");
        return;
    }
    if((stack-&gt;top + 1) &lt; stack-&gt;size)
    {
        stack-&gt;mem[stack-&gt;top + 1] = c;
        stack-&gt;top++;
    }
    return;
}

char stack_pop(STACK *stack)
{
    assert(stack);

    if(stack-&gt; top &lt; 0)
    {
        fprintf(stderr, "Cannot pop from empty stack.\n");
        return -1;
    }

    char o;
    o = stack-&gt;mem[stack-&gt;top];
    stack-&gt;top--;
    return o;
}

char stack_peek(STACK * const stack)
{
    assert(stack);

    if(stack-&gt;top &lt; 0)
    {
        fprintf(stderr, "Nothing on stack to peek at.\n");
        return -1;
    }
    return stack-&gt;mem[stack-&gt;top];
}

void stack_print(STACK *stack)
{
    assert(stack);
    if(!stack-&gt;top)
    {
        fprintf(stderr, "Nothing on stack to print.\n");
        return;
    }
    printf("Entire stack:\n");
    int i;
    for(i = stack-&gt;top; i &gt;= 0; --i)
    {
        printf("%c\n", stack-&gt;mem[i]);
    }
}


Please provide feedback on whatever you see fit, however, error handling would be a helpful focus.
You could allow to initialize the stack locally.
Just by separating the bigger part of stack_create to separate function.

void stack_init(STACK* stack, char* mem, size_t size)
{
    stack-&gt;mem = mem;
    stack-&gt;size = size;
    stack-&gt;top = -1;
}

char stack_mem[STACK_SIZE];
STACK stack;
stack_init(&amp;stack, stack_mem, STACK_SIZE);


As for error handling, stack usualy does not handle it. You should instead offer the "is empty?" and "is full?" functions. Consumers of the stack are then responsible for making sure stack is not empty before they peek or pop, and that it is not full before they push. Check for emptiness is often integral part of algorithms using the stack anyway. Check for fullness not so much, it Is mostly limitation of a fixed size stack. But the consumer must check it nevertheless or they must be sure that bigger stack is never needed.

In stack_create you should check if malloc returns null then dont assign to properties and return null as well. Consumers are again responsible for checking this situation.

One last point, the stack_create could be complemented with stack_destroy.