C言語でスタックを実装をしようと思い、いくつかのwebページを参考にして、以下のように実装しました(このコードは正常に動作します)。

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

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

stack *stack_root = NULL;


// stackの先頭の要素をとり出す
int pop(void){
  int n;
  stack *next, *fr;

  next = calloc(1, sizeof(stack));
  if(next == NULL){
    printf("ERROR\n");
    exit(-1);
  }

  // NULLポインタならエラー(-1)を返す
  if(stack_root == NULL){
    return -1;
  }

  n = stack_root->data;
  next = stack_root->next;
  fr = stack_root;
  stack_root = next;
  free(fr);

  return n;
}


// stackの先頭に要素を追加
void push(int n){
  stack *new_stack;

  new_stack = calloc(1, sizeof(stack));
  if(new_stack == NULL){
    printf("ERROR\n");
    exit(-1);
  }

  new_stack->data = n;
  new_stack->next = stack_root;
  stack_root = new_stack;
}


int main(){
  int i;

  push(3);
  push(2);

  i = pop();
  printf("%d \n", i);

  i = pop();
  printf("%d \n", i);

  i = pop();
  printf("%d \n", i);

  push(2);

  i = pop();
  printf("%d \n", i);
}

/*実行結果
2 
3 
-1 
2
*/ 

しかしながらこのコードだと、スタックを複数使用する場合、その個数分stack_root(スタックの先頭)をグローバル領域で定義しなければいけません。
そこで、stack_rootの宣言をメイン関数内に移動し、以下のようなコードを書きました(このコードは正常に動作しません)。

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

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


// stackの先頭の要素をとり出す
int pop(stack *stack_root){
  int n;
  stack *next, *fr;

  next = calloc(1, sizeof(stack));
  if(next == NULL){
    printf("ERROR\n");
    exit(-1);
  }

  // NULLポインタならエラー(-1)を返す
  if(stack_root == NULL){
    return -1;
  }

  n = stack_root->data;
  next = stack_root->next;
  fr = stack_root;
  stack_root = next;
  free(fr);

  return n;
}


// stackの先頭に要素を追加
void push(stack *stack_root, int n){
  stack *new_stack;

  new_stack = calloc(1, sizeof(stack));
  if(new_stack == NULL){
    printf("ERROR\n");
    exit(-1);
  }

  new_stack->data = n;
  new_stack->next = stack_root;
  stack_root = new_stack;
}


int main(){
  int i;
  stack *stack_root = NULL;

  push(stack_root, 3);
  push(stack_root, 2);

  i = pop(stack_root);
  printf("%d \n", i);

  i = pop(stack_root);
  printf("%d \n", i);

  i = pop(stack_root);
  printf("%d \n", i);

  push(stack_root, 2);

  i = pop(stack_root);
  printf("%d \n", i);
}

/*実行結果

-1 
-1 
-1 
-1 

*/

pop関数やpush関数で関数の先頭のポインタを渡せば良いと思ったのですが、何故このコードは正常に動作しないのでしょうか。教えてください。


追記

774RRさん、ありがとうございます。
ポインタ変数の場合も、通常の変数と同様、関数に参照渡ししたい場合はその変数のポインタを渡さなければならない

#include <stdio.h>

// 通常の変数を参照渡し
void addone(int *n){
  (*n)++;
}

int main(void){
  int i=1;

  addone(&i);

  printf("%d\n", i);
  return 0;
}

// 実行結果
// 2

,

// ポインタ変数を参照渡し
void addone(int **n){
  (**n)++;
}

int main(void){
  int *i;

  *i = 1;
  addone(&i);

  printf("%d\n", *i);
  return 0;
}

// 実行結果
// 2

ということですね。
したがって、

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

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


// stackの先頭の要素をとり出す
int pop(stack **stack_root){
  int n;
  stack *next, *fr;

  next = calloc(1, sizeof(stack));
  if(next == NULL){
    printf("ERROR\n");
    exit(-1);
  }

  // NULLポインタならエラー(-1)を返す
  if(*stack_root == NULL){
    return -1;
  }

  n = (*stack_root) -> data;
  next = (*stack_root) -> next;
  fr = *stack_root;
  *stack_root = next;
  free(fr);

  return n;
}


// stackの先頭に要素を追加
void push(stack **stack_root, int n){
  stack *new_stack;

  new_stack = calloc(1, sizeof(stack));
  if(new_stack == NULL){
    printf("ERROR\n");
    exit(-1);
  }

  new_stack->data = n;
  new_stack->next = *stack_root;
  *stack_root = new_stack;

}


int main(){
  int i;
  stack *stack_root = NULL;

  push(&stack_root, 3);
  push(&stack_root, 2);

  i = pop(&stack_root);
  printf("%d \n", i);

  i = pop(&stack_root);
  printf("%d \n", i);

  i = pop(&stack_root);
  printf("%d \n", i);

  push(&stack_root, 1);

  i = pop(&stack_root);
  printf("%d \n", i);

  return 0;
}

/*実行結果

2 
3 
-1 
1 

*/

というようにして、ポインタ変数のポインタをpop()やpush()に渡すことで解決しました。
ありがとうございました。