Implement a stack using a linked list I am to implement a stack in C using a linked list. My code is below. My design:


I use stack_ prefix to each function, and each function has a Stack* self pointer. This is similar to object-oriented languages’ bundling of data and methods. I do not know how idiomatic this is in C.
Each function returns a bool indicating success or failure. A more detailed implementation would have error codes and messages. I do not know how idiomatic this is in C.
The stack_init function does not call malloc, so that one could define an instance of Stack locally, as in the test methods below.
I used int as the type of data, but this could be modified for other types. I did not use void* since void* would lose type safety.


stack.h

#ifndef STACK_H_
#define STACK_H_

#include &lt;stdbool.h&gt;

typedef struct Node {
  int data;
  struct Node* next;
} Node;

typedef struct {
    Node* root;
} Stack;

bool stack_init(Stack* self);
bool stack_delete(Stack* self);
bool stack_pop(Stack* self, int* data);
bool stack_push(Stack* self, int data);

#endif  // STACK_H_


stack.c

#include &lt;stdlib.h&gt;
#include "stack.h"

bool stack_init(Stack* self) {
  if (self == NULL) {
    return false;
  }

  self-&gt;root = NULL;
  return true;
}

bool stack_delete(Stack* self) {
  Node* next;
  while (self-&gt;root != NULL) {
    next = self-&gt;root-&gt;next;
    free(self-&gt;root);
    self-&gt;root = next;
  }

  return true;
}

bool stack_push(Stack* self, int data) {
  if (self == NULL) {
    return false;
  }

  Node* node = malloc(sizeof(Node));
  if (node == NULL) {
    return false;
  }

  node-&gt;data = data;
  node-&gt;next = self-&gt;root;
  self-&gt;root = node;

  return true;
}

bool stack_pop(Stack* self, int* data){
  Node* root = self-&gt;root;
  if (root == NULL) {
    return false;
  }

  *data = root-&gt;data;
  self-&gt;root = root-&gt;next;
  free(root);

  return true;
}


stack_test.c

#include &lt;assert.h&gt;
#include "stack.h"

static void test_null_stack_should_fail_to_init() {
  // arrange
  Stack* stack = NULL;

  // act
  assert(!stack_init(stack));
}

static void test_empty_stack() {
  // arrange
  Stack stack;

  // act
  assert(stack_init(&amp;stack));
  assert(stack_delete(&amp;stack));
}

static void test_push_then_pop() {
  // arrange
  Stack stack;
  stack_init(&amp;stack);
  stack_push(&amp;stack, 1);
  stack_push(&amp;stack, 2);
  stack_push(&amp;stack, 3);
  int data;

  // act
  assert(stack_pop(&amp;stack, &amp;data));
  assert(data == 3);
  assert(stack_pop(&amp;stack, &amp;data));
  assert(data == 2);
  assert(stack_pop(&amp;stack, &amp;data));
  assert(data == 1);
  assert(!stack_pop(&amp;stack, &amp;data));
  assert(stack_delete(&amp;stack));
}

static void test_push() {
  // arrange
  Stack stack;
  stack_init(&amp;stack);
  stack_push(&amp;stack, 1);
  stack_push(&amp;stack, 2);
  stack_push(&amp;stack, 3);

  // act
  assert(stack_delete(&amp;stack));
}

int main() {
  test_null_stack_should_fail_to_init();
  test_empty_stack();
  test_push_then_pop();
  test_push();
}
stack_delete only returns true. If a return value from a function carries no information, better make it void.
There is no consistency in testing parameters. Some functions test for self not being null, other directly proceed to calculating self-&gt;root.
The Stack existence is only justifiable if it has more data members than just root. As @glampert mentioned in comments, tracking size is one of the candidates. On the other hand,  abandoning Stack for good, and letting client manage the root, as in

Node * stack_push(Node * root, data);
....

root = stack_push(root, data);


is also a viable strategy.
It would be nice to provide a non-destructive top() interface to access the root data. There is even a popular belief that a pop-like function should only pop an element from the top of stack (as opposed to pop element and return data); in this school of thought top() is a must.