12825


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

[code]

    #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
    */ 
    
[/code]

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

[code]

    #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 
    
    */
    
[/code]

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

  

## 追記

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

[code]

    #include <stdio.h>
    
    // 通常の変数を参照渡し
    void addone(int *n){
      (*n)++;
    }
    
    int main(void){
      int i=1;
    
      addone(&i);
    
      printf("%d\n", i);
      return 0;
    }
    
    // 実行結果
    // 2
    
[/code]

,

[code]

    // ポインタ変数を参照渡し
    void addone(int **n){
      (**n)++;
    }
    
    int main(void){
      int *i;
    
      *i = 1;
      addone(&i);
    
      printf("%d\n", *i);
      return 0;
    }
    
    // 実行結果
    // 2
    
[/code]

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

[code]

    #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 
    
    */
    
[/code]

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


CC BY-SA 3.0
2015-07-30T07:16:41.417
12824
2015-07-30T09:10:09.923
2020-06-17T08:14:45.997
-1
10725
question
c ポインタ スタック
C言語でのスタックを実装する際、何故グローバル領域でスタックの先頭のポインタを宣言しなければいけないのでしょうか。


[c](/questions/tagged/c "'c' のタグが付いた質問を表示") で関数の引数は値渡し（複写渡し）であるため、です。  
提示コード `main` 側は

[code]

    push(stack_root, 3);
    
[/code]

で `stack_root` が変更されて帰ってくることを期待している構造になっています。  
が [c](/questions/tagged/c "'c' のタグが付いた質問を表示") の文法規則上、この呼び出しでは `stack_root`
は変更されないのです。

`push` で行っている

[code]

    stack_root = new_stack;
    
[/code]

は `main` の `stack_root` とは異なる局所変数 `stack_root` を書き換えるだけです。  
結果的に `main` の `stack_root` は変化しないままです。

大域変数だったら両者が同一なのでうまくいくわけです。

ではどうするか・・・なんですが、やりかたはいくつもあります。

策１ `push` `pop` には `&stack_root` を渡す。  
`push` `pop` は `*stack_root` を操作する。

ってとこでしょうか。他の方策も考えてみてください。


CC BY-SA 3.0
2015-07-30T07:35:55.717
12825
2015-07-30T07:35:55.717
8589
12824
answer