質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.31%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

Q&A

解決済

2回答

744閲覧

C言語におけるスタックの実装

kouchan_dd

総合スコア20

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

0グッド

0クリップ

投稿2023/11/09 12:00

編集2023/11/12 14:59

実現したいこと

以下の問題のプログラムを作成したいです。

スタック1,2があり,図1,2の状態になっている。
• 関数fはスタック1からPopしたデータをそのままスタック2にPushする。
• 関数gはスタック2からPopしたデータを出力する。

s1[4]
s1[3]1
s1[2]2
s1[1]3
s1[0]4

図1. スタック1

s2[4]
s2[3]
s2[2]
s2[1]
s2[0]

図2. スタック2

このとき,次の機能が実装されているプログラムを作成しなさい。

(1)初期状態の作成

C

1int main(void) 2{ 3 int x; 4 IntStack s1, s2; 5 6 if (Initialize(&s1, 5) == -1) { 7 puts("スタックの生成に失敗しました。"); 8 return 1; 9 } 10 if (Initialize(&s2, 5) == -1) { 11 puts("スタックの生成に失敗しました。"); 12 return 1; 13 } 14 15 Push(&s1, 4); 16 Push(&s1, 3); 17 Push(&s1, 2); 18 Push(&s1, 1);

(2)関数fとgを作成

C

1void f(IntStack *s1, IntStack *s2) 2{ 3 int x; 4 Pop(s1, &x); 5 Push(s2, x); 6} 7 8int g(IntStack *s2) 9{ 10 int x; 11 Pop(s2, &x); 12 return x; 13}

※なお上記のPop、Push内の()の中身は私がこの問題を解いた際に一応入れたものです。間違っているかもしれません。

(3)f,f,g,gの順に実行

[実行結果]
(初期状態)
s1[4]=0 s2[4]=0
s1[3]=1 s2[3]=0
s1[2]=2 s2[2]=0
s1[1]=3 s2[1]=0
s1[0]=4 s2[0]=0

fを実行
s1[4]=0 s2[4]=0
s1[3]=0 s2[3]=0
s1[2]=2 s2[2]=0
s1[1]=3 s2[1]=0
s1[0]=4 s2[0]=1

fを実行
s1[4]=0 s2[4]=0
s1[3]=0 s2[3]=0
s1[2]=0 s2[2]=0
s1[1]=3 s2[1]=2
s1[0]=4 s2[0]=1

gを実行、出力は2
s1[4]=0 s2[4]=0
s1[3]=0 s2[3]=0
s1[2]=0 s2[2]=0
s1[1]=3 s2[1]=0
s1[0]=4 s2[0]=1

gを実行、出力は1
s1[4]=0 s2[4]=0
s1[3]=0 s2[3]=0
s1[2]=0 s2[2]=0
s1[1]=3 s2[1]=0
s1[0]=4 s2[0]=0

2 -> 1の順に出力。

前提

C言語で上記のようなプログラムを作っています。
実行結果がうまく表示されません。
下に実行画面を表示します。

発生している問題・エラーメッセージ

s1[3] = 1 s2[3] = 1 s1[2] = 2 s2[2] = 2 s1[1] = 3 s2[1] = 3 s1[0] = 4 s2[0] = 4 fを実行。 s1[2] = 2 s2[2] = 2 s1[1] = 3 s2[1] = 3 s1[0] = 4 s2[0] = 4 s1[0] = 1 s2[0] = 1 fを実行。 s1[1] = 3 s2[1] = 3 s1[0] = 4 s2[0] = 4 s1[1] = 2 s2[1] = 2 s1[0] = 1 s2[0] = 1 gを実行。出力は2。 s1[0] = 1 s2[0] = 1 gを実行。出力は1。 続行するには何かキーを押してください . . .

該当のソースコード

C

1#include <stdio.h> 2#include <stdlib.h> 3 4typedef struct { 5 int max; 6 int ptr; 7 int *stk; 8} IntStack; 9 10int Initialize(IntStack *s, int max) 11{ 12 s->ptr = 0; 13 if ((s->stk = calloc(max, sizeof(int))) == NULL) { 14 s->max = 0; 15 return -1; 16 } 17 s->max = max; 18 19 return 0; 20} 21 22int Push(IntStack *s, int x) 23{ 24 if (s->ptr >= s->max) 25 return -1; 26 s->stk[s->ptr++] = x; 27 28 return 0; 29} 30 31int Pop(IntStack *s, int *x) 32{ 33 if (s->ptr <= 0) 34 return -1; 35 36 s->ptr--; 37 *x = s->stk[s->ptr]; 38 s->stk[s->ptr] = 0; 39 40 return 0; 41} 42 43void Print(const IntStack *s) 44{ 45 int i; 46 for(i = s->ptr - 1; i >= 0; i--) { 47 printf(" s1[%d] = %3d s2[%d] = %3d\n", i, s->stk[i], i, s->stk[i]); 48 } 49 putchar('\n'); 50} 51 52void Terminate(IntStack *s) 53{ 54 if (s->stk != NULL) 55 free(s->stk); 56 s->max = s->ptr = 0; 57} 58 59void f(IntStack *s1, IntStack *s2) 60{ 61 int x; 62 Pop(s1, &x); 63 Push(s2, x); 64} 65 66int g(IntStack *s2) 67{ 68 int x; 69 Pop(s2, &x); 70 return x; 71} 72 73int main(void) 74{ 75 int x; 76 IntStack s1, s2; 77 78 if (Initialize(&s1, 5) == -1) { 79 puts("スタックの生成に失敗しました。"); 80 return 1; 81 } 82 if (Initialize(&s2, 5) == -1) { 83 puts("スタックの生成に失敗しました。"); 84 return 1; 85 } 86 87 Push(&s1, 4); 88 Push(&s1, 3); 89 Push(&s1, 2); 90 Push(&s1, 1); 91 92 Print(&s1); 93 printf("fを実行。\n"); 94 f(&s1, &s2); 95 Print(&s1); 96 Print(&s2); 97 printf("fを実行。\n"); 98 f(&s1, &s2); 99 Print(&s1); 100 Print(&s2); 101 printf("gを実行。出力は2。\n"); 102 g(&s2); 103 Print(&s2); 104 printf("gを実行。出力は1。\n"); 105 g(&s2); 106 Print(&s2); 107 108 Terminate(&s1); 109 Terminate(&s2); 110 111 return 0; 112}

試したこと

Terminate関数の作成まではスタックの作成方法のやり方を忠実に行った。

関数fとgの内容を修正し、今までエラーメッセージが出力され、実行画面が表示されなかったところを実行画面が表示されるようになった。

s1とs2の値が変わっていないため、Print関数の内容か、main関数内のPrint関数の使い方に問題があると考えている。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ikedas

2023/11/09 13:08 編集

- 「発生している問題・エラーメッセージ」に記載されているメッセージは、どのような物が出力しているのでしょうか。一般的なコンパイラが出力する警告などのようには見えません。 - これは学校の課題か何かでしょうか。であれば、使っている教科書の書誌情報 (著者名、標題、出版社名、参照したページ番号) を明記してください。 いずれも、このコメント欄に書くのではなく、質問文を編集して書いてください。
melian

2023/11/09 13:50

ccls でチェックする(static code analysis)と多数の notes, warning, error が検出されます。 例えば Pop(), Push() 関数の signature は以下の様になっていますが、  int Pop(IntStack *s, int *x)  int Push(IntStack *s, int x) f() 関数での呼び出しは以下の通りで、一つも合っていません……。  void f(IntStack *s1, IntStack *s2)  {   int x;   Pop(&s1, &s2);   Push(&s2, &x);  }
jimbe

2023/11/09 19:15 編集

変数、値、アドレス、ポインタ、引数、仮引数を何処まで理解されているでしょう? タイトルは『スタックの実装』となっていますが、実装はお手本通りそのまま書けていて、しかしそのスタックを使おうとしたが『アドレスとかポインタとか関数の使い方が分からない』というのが実際のようです。
guest

回答2

0

間違いは多数ありますね。とりあえず、警告の出ている62行目について言うと、

void f(IntStack *s1, IntStack *s2)
Pop(&s1, &s2);

Popの第一引数にIntStack型へのポインターのポインター、第二引数にもIntStack型へのポインターのポインターを渡していますが、Popの宣言を見ると、

int Pop(IntStack *s, int *x)

と、第一引数はIntStack型へのポインターで、第二引数はintへのポインターで、どちらも会っていません。

63行目と69行目についても同様。
94行目はメッセージの通り、引数の個数から間違っています。

型やポインターに関する理解が間違っているのかと思いましたが、それもありますが、それだけでなく、
どちらかというと、あまり考えずに書いているとか、注意力不足とか、タイプミスとか、タイプミスが無いか見直していないとか、そのあたりですね。

あまり考えてないというのは、例えばPrint関数の定義や機能も、この関数が一帯どういう機能を持った関数なのかを書いているうちにあやふやになってしまっているようです。Terminateもそうですね。
「この関数はどういう機能を持っていて、引数の意味は何で、引数の型は何か」をちゃんと文章で書いてから、それを見ながらプログラムを書くのが良いと思います。

あと、エラーメッセージや警告メッセージ、結果が期待通りで無い場合は、まず最初に「タイプミスが無いこと100万円賭けられる」と確信が持てるまでタイプミス(や、コピペ後の修整ミス)をチェックしましょう。
その他のミスを探すのはその後です。

ポインターに関しての理解が間違っているというのは、例えばtttという型があって、ttt *x;と宣言されているとすると、*xttt型で、xtttへのポインターで、&xtttへのポインターへのポインターですが、そのあたりが分かってないのだと思います。

引数の個数については、まさか、putchar('A', 'B');と書くとputchar('A'); putchar('B');と書いたのと同じになるとか思ってませんよね?

色々書きましたが、このプログラムを自力でゼロから書いたのであれば、ちゃんと書けている関数もあるし、
全体的にわりと惜しいところまで出来ているので、プログラミング力がないという事は無さそうです。
ポインターの復習をした上で、十分注意して書き直せば出来そうな気がします。頑張ってください。

コード内じゃないですが、質問文の図2の表の中はおそらく s2 と書くつもりだったところが s1 になっています。これもコピペ後の修正ミス(修正漏れ)でしょうね。

投稿2023/11/09 15:58

otn

総合スコア86295

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

ベストアンサー

コードを修正されてエラーメッセージは無くなった感じでしょうか。
問題は、 Print 関数の

c

1printf(" s1[%d] = %3d s2[%d] = %3d\n", i, s->stk[i], i, s->stk[i]);

で s1 としているほうにも s2 としているほうにも s の値を使っている点でしょう。
一回の Print の呼び出しを二回にしてそれぞれにスタックを渡すようにすることでエラーメッセージを消したようですが、修正の方向が違います。
Print で二つのスタックを表示するのなら、呼ぶ側でパラメータとして二つのスタックを渡すのは正しく、呼ばれる側の Print 関数側がパラメータとして二つのスタックを受け取るようになっていなかった のが問題だったのです。

Print の件数が少なくなるのは、 printf を囲む for 文がそのように書いてあるからです。
スタックの各変数が動作によってどのように変化するのか、 Print 時にはどの変数が幾つになっていて、欲しい結果の為にはどの変数を使う必要があるのかを考えてみてください。

それともう一つ、 g 関数は『スタック2からPopしたデータを出力する』が仕様です。
この場合の出力とは (関数値として return することでは無く) 『表示』のことです。

あくまで想像ですが、実行結果として書かれているのは"そのまま日本語部分までそのようにしろ"ということでは無いのではないでしょうか。
実際の表示は以下で良いように思います。

s1[4]=0 s2[4]=0 s1[3]=1 s2[3]=0 s1[2]=2 s2[2]=0 s1[1]=3 s2[1]=0 s1[0]=4 s2[0]=0 s1[4]=0 s2[4]=0 s1[3]=0 s2[3]=0 s1[2]=2 s2[2]=0 s1[1]=3 s2[1]=0 s1[0]=4 s2[0]=1 s1[4]=0 s2[4]=0 s1[3]=0 s2[3]=0 s1[2]=0 s2[2]=0 s1[1]=3 s2[1]=2 s1[0]=4 s2[0]=1 2 s1[4]=0 s2[4]=0 s1[3]=0 s2[3]=0 s1[2]=0 s2[2]=0 s1[1]=3 s2[1]=0 s1[0]=4 s2[0]=1 1 s1[4]=0 s2[4]=0 s1[3]=0 s2[3]=0 s1[2]=0 s2[2]=0 s1[1]=3 s2[1]=0 s1[0]=4 s2[0]=0

投稿2023/11/12 16:18

編集2023/11/12 16:39
jimbe

総合スコア13318

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.31%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問