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

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

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

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

ファイル

ファイルとは、文字列に基づいた名前又はパスからアクセスすることができる、任意の情報のブロック又は情報を格納するためのリソースです。

Xcode

Xcodeはソフトウェア開発のための、Appleの統合開発環境です。Mac OSXに付随するかたちで配布されています。

Q&A

解決済

4回答

2155閲覧

スタックがオーバーフローにならないために

VanS

総合スコア7

C

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

ファイル

ファイルとは、文字列に基づいた名前又はパスからアクセスすることができる、任意の情報のブロック又は情報を格納するためのリソースです。

Xcode

Xcodeはソフトウェア開発のための、Appleの統合開発環境です。Mac OSXに付随するかたちで配布されています。

0グッド

0クリップ

投稿2020/06/05 16:38

編集2020/06/05 17:53

前提・実現したいこと

スタック型計算機を作成しています。キーボードとファイルの両方からデータ入力をしたいのですが、ファイルからのデータ入力がうまくいきません。

ファイル:data.txtには 9 1 - 3 * 7 - と書いてあります。

もっともファイルからのデータの入力についてもいまいちです...

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

五行目で定義したスタックサイズを七行目で定義したスタックのポインタが上回り、stack overflowになります。

該当のソースコード

C

1 2#include <stdio.h> 3#include <stdlib.h> 4#include <ctype.h> 5 6typedef long ELEMENT; 7 8#define STACK_SIZE 100 9 10ELEMENT stack[STACK_SIZE]; 11int n; 12 13/*push関数**/ 14void push(ELEMENT x) 15{ 16 if (n>=STACK_SIZE){ 17 printf("stack overflow"); /*ファイルからの入力を試みた際に、このエラーメッセージが表示されます。*/ 18 exit(1); 19 } 20 stack[n++]=x; 21} 22 23/*pop関数*/ 24ELEMENT pop() 25{ 26 if (n<=0){ 27 printf("stack underflow"); 28 exit(1); 29 } 30 return stack[--n]; 31} 32 33int main(void) 34{ 35 n=0; /*スタックの初期化*/ 36 37 int s; 38 printf("数字データの時はスタックに積み、ピリオドの時はスタックから降ろします。(EOFで終了)\n"); 39 printf("データ入力の方法を選んでください。\n0..キーボード/1..ファイル:"); 40 scanf("%d",&s); 41 int c ; 42 long x,a,b; 43 FILE *fp; 44 45 if (s==0){ 46 while((c=getchar())!=EOF){ 47 48 if (isdigit(c)){ 49 ungetc(c,stdin); 50 scanf("%ld",&x); 51 push(x); 52 }else{ 53 switch (c){ 54 case '.': 55 pop(); 56 break; 57 case '+': 58 b=pop();a=pop(); 59 push(a+b); 60 break; 61 case '-': 62 b=pop();a=pop(); 63 push(a-b); 64 break; 65 case '*': 66 b=pop();a=pop(); 67 push(a*b); 68 break; 69 case '/': 70 b=pop();a=pop(); 71 push(a/b); 72 break; 73 case ' ': 74 break; 75 case '\n': 76 if(n!=0) 77 printf("答えは%ldです。",pop()); 78 n=0; 79 break; 80 81 default: 82 printf("不正な入力です。\n"); 83 while ((c=getchar())!=EOF && c!='a') 84 ; 85 break; 86 } 87 } 88 } 89 90 91 }else if (s==1){ /*ファイルからの入力*/ 92 char data[100]={0}; /*訂正箇所:0クリア*/ 93 int ch; 94 int q=1; 95 96 fp=fopen("data.txt","r"); 97 if (fp==NULL){ 98 printf("ファイルオープン失敗\n"); 99 return 1; 100 } 101 102 for (int i=0;i<100;i++){ 103 data[i]=fgetc(fp); 104 if (data[i]==EOF) break; 105 } 106 107 for (int i=0;data[i]!='\0';i++){ 108 if (isdigit(data[i])){ 109 push(data[i]); 110 }else{ 111 switch (data[i]){ 112 case '.': 113 pop(); 114 break; 115 case '+': 116 b=pop();a=pop(); 117 push(a+b); 118 break; 119 case '-': 120 b=pop();a=pop(); 121 push(a-b); 122 break; 123 case '*': 124 b=pop();a=pop(); 125 push(a*b); 126 break; 127 case '/': 128 b=pop();a=pop(); 129 push(a/b); 130 break; 131 case ' ': 132 break; 133 case '\n': 134 if(n!=0) 135 printf("答えは%ldです。",pop()); 136 n=0; 137 break; 138 139 default: 140 push(data[i]); 141 break; 142 } 143 } 144 145 146 } 147 fclose(fp); 148 } 149 return 0; 150} 151

試したこと

5、7行目のサイズを変えてみましたがダメでした

補足情報(FW/ツールのバージョンなど)

Xcode Version 11.2 (11B52)

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

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

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

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

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

guest

回答4

0

何が直接的な問題かは既に指摘があるので割愛しますが、そもそもの問題として、キーボード/ファイルで処理を分けるのは良い設計とは言えません。

scanfやgetcharは別に「キーボード入力を処理する」APIではありません。
キーボード入力を通常のファイル入力と同じ方法で扱えるように実行環境の方で調整が入っていて、標準のFILE*変数であるstdinを裏で使った入力処理を行っているだけです。なので、「キーボード入力を処理する『つもり』」で書いたコードは、そっくりそのまま「ファイル入力を処理する」に転用できます。変に分けると却ってバグの元です。

getchar()getc(stdin), scanf(フォーマット,…)fscanf(stdin,フォーマット,…)

という等価な対応があることに注意してください。このstdinfpに替えればそのままファイル入力処理になる、ということです。もちろん、fpを使えるのはfopen()の後です。
※関数化するか、変数を間に挟んで処理を共通化するのが分かり易いでしょう。

投稿2020/06/05 18:06

angel_p_57

総合スコア1672

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

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

VanS

2020/06/06 07:54

なるほど...。そのあたり、曖昧な理解のままでした。ありがとうございます。!
pepperleaf

2020/06/06 08:11

> このstdinをfpに替えれば いや、変えなくても、実行時に、"program < file" とすれば、fileからの読み込みとなります。元々、入出力デバイス(キーボード etc)は、特殊なファイル(と言うのが、Unix(Linx)の考え) とは言うものの、EOFの扱いとかが、微妙に違って困るのは、Windows?
angel_p_57

2020/06/06 09:41

> いや、変えなくても、実行時に、"program < file" とすれば、fileからの読み込みとなります。 「リダイレクト」によって標準入力の接続先を変えるって話も関連としてありますけど、取り敢えず「sの入力によってキーボード入力のデータを処理するか、特定のファイルのデータを処理するか切り替える」って設計してるんだから、まずはそれを尊重しましょうよ…。 ※ cat だとか様々なコマンドで「ファイルが指定されればファイルを処理、なければ標準入力からのデータを処理」ってあるでしょ。
VanS

2020/06/06 13:38

解決できました。皆様本当にありがとうございました..!!
guest

0

  • 問題点1

char data[100]を0クリアしていない。
不定値の為、ファイル終端以降どこでdata[i] != '\0'が成立するかはメモリの状況次第です。

  • 問題点2

while (q)が不要。
無限ループしています。


  • 問題点3

kazuma-sさんも指摘されていますが、数字を文字コードのまま格納しています。
09をそのまま格納しているため、数値としては4857が格納されています。


何も表示されることなくプログラムが終了してしまいます

こちらでも今貼られているコードをXcodeで動かしてみましたが
問題点3により57 49 - 51 * 55 -となるため計算結果は間違っているものの、結果は表示されています。
Xcode

投稿2020/06/05 17:25

編集2020/06/06 09:10
SHOMI

総合スコア4079

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

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

VanS

2020/06/05 17:48

ご丁寧な説明ありがとうございます。ご指摘いただいた点を修正してみましたが実行しても何も表示されることなくプログラムが終了してしまいます...
SHOMI

2020/06/06 00:05 編集

>ファイル:data.txtには 9 1 - 3 * 7 - と書いてあります。 '\n'で結果表示していますが、9 1 - 3 * 7 -の後に改行してありますか?
VanS

2020/06/06 07:54

失礼しました。改行も含んであります..
SHOMI

2020/06/06 08:41 編集

こちらでも今貼られているコードをXcodeで動かしてみましたが表示されましたよ。 数字部分がアスキーコードの値で計算されているため、"57 49 - 51 * 55 -"となり計算結果は353と表示されましたが…
guest

0

ベストアンサー

現在のコードでスタックオーバーフローにならないようですが、
どういう入力で現象を再現できますか?

また、キーボード入力の場合は、scanf("%ld",&x); で数値を読み込んでいるのに、
ファイル入力場合、push(data[i]); で数字を数値に変換せずに使っているのはなぜですか?

キーボード入力とファイル入力を統一的に扱うと次のようになります。

C

1#include <stdio.h> // printf, puts, scanf, fscanf, fgetc, fopen, fclose, perror 2#include <stdlib.h> // exit 3#include <ctype.h> // isdigit 4 5typedef long ELEMENT; 6 7#define STACK_SIZE 100 8 9ELEMENT stack[STACK_SIZE]; 10int n; 11 12void push(ELEMENT x) 13{ 14 if (n >= STACK_SIZE) { 15 puts("stack overflow"); 16 exit(1); 17 } 18 stack[n++]=x; 19} 20 21ELEMENT pop() 22{ 23 if (n <= 0) { 24 puts("stack underflow"); 25 exit(1); 26 } 27 return stack[--n]; 28} 29 30int main(void) 31{ 32 printf("数字データの時はスタックに積み、\n" 33 "ピリオドの時はスタックから降ろします。(EOFで終了)\n" 34 "データ入力の方法を選んでください。\n" 35 "0..キーボード/1..ファイル:"); 36 int c; 37 scanf("%d",&c); 38 FILE *fp = stdin; 39 if (c == 1) { 40 fp = fopen("data.txt", "r"); 41 if (fp == NULL) { perror("data.txt"); return 1; } 42 } 43 while ((c = fgetc(fp)) != EOF) { 44 long x; 45 if (isdigit(c)) { 46 ungetc(c, fp); 47 fscanf(fp, "%ld", &x); 48 push(x); 49 } else { 50 switch (c) { 51 case '.': pop(); break; 52 case '+': x = pop(); push(pop() + x); break; 53 case '-': x = pop(); push(pop() - x); break; 54 case '*': x = pop(); push(pop() * x); break; 55 case '/': x = pop(); push(pop() / x); break; 56 case ' ': break; 57 case '\n': 58 if (n > 0) printf("答えは%ldです。\n", pop()); 59 n = 0; 60 break; 61 default: 62 puts("不正な入力です。"); 63 while ((c = fgetc(fp)) != EOF && c != 'a') ; 64 break; 65 } 66 } 67 } 68 return 0; 69}

投稿2020/06/06 03:53

kazuma-s

総合スコア8224

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

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

SHOMI

2020/06/06 05:01 編集

>現在のコードでスタックオーバーフローにならない 質問文に貼られていたコードに対して直接指摘内容を書き換えされているためです…
VanS

2020/06/06 07:53

SHOMIさんのおっしゃっている通りです。わかりづらくしてすみません...。 ご丁寧にありがとうございます...!
VanS

2020/06/06 08:18

kazuma-s様のコードをほぼ参考に書き直させてもらったのですが、ファイル入力を選択すると何も表示されないまま終了してしまいました.... data.txtに問題があるのでしょうか? 9 1 - 3 * 7 - \nと書いてあるだけなのですが...
SHOMI

2020/06/06 08:42 編集

もしかしてエンターキー入力ではなく、\nと2文字で書いていませんか…? こちらでもXcodeで動かしてみましたが結果表示されました。
VanS

2020/06/06 13:06

あ、\nとふた文字?で書いてしまってます... エンターキー入力とは何でしょうか...? 無知で申し訳ありません。
SHOMI

2020/06/06 13:12 編集

普通にエンターキー(リターンキー)を押してください。 キーボード入力で動かす時と同じです。
VanS

2020/06/06 13:34

なるほど...!!!できました!ご丁寧にありがとうございました!!
VanS

2020/06/06 13:40

皆様、ご親切に教えてくださったのでベストアンサーなるものを1つ選ぶのは難しいですが..
guest

0

for (int i=0;data[i]!='\0';i++){

ファイルから読み込み後、'\0'まで処理してるつもりでしょうけど、ファイルの読み込みで、'\0'を格納してるところはありません。
なので、dataのサイズを超えて処理が回ってしまいます

投稿2020/06/05 17:24

y_waiwai

総合スコア87774

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

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

VanS

2020/06/05 17:48

ご丁寧な説明ありがとうございます。ご指摘いただいた点を修正してみましたが実行しても何も表示されることなくプログラムが終了してしまいます...
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問