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

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

ただいまの
回答率

88.83%

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

解決済

回答 4

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 422

VanS

score 5

前提・実現したいこと

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

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

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

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

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

該当のソースコード

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

typedef long ELEMENT;

#define STACK_SIZE 100

ELEMENT stack[STACK_SIZE];
int n;

/*push関数**/
void push(ELEMENT x)
{
    if (n>=STACK_SIZE){
        printf("stack overflow");      /*ファイルからの入力を試みた際に、このエラーメッセージが表示されます。*/
        exit(1);
    }
    stack[n++]=x;
}

/*pop関数*/
ELEMENT pop()
{
    if (n<=0){
        printf("stack underflow");
        exit(1);
    }
    return stack[--n];
}

int main(void)
{
    n=0;            /*スタックの初期化*/

    int s;
    printf("数字データの時はスタックに積み、ピリオドの時はスタックから降ろします。(EOFで終了)\n");
    printf("データ入力の方法を選んでください。\n0..キーボード/1..ファイル:");
    scanf("%d",&s);
    int c ;
    long x,a,b;
    FILE *fp;

    if (s==0){
        while((c=getchar())!=EOF){

            if (isdigit(c)){
                ungetc(c,stdin);
                scanf("%ld",&x);
                push(x);
            }else{
                switch (c){
                    case '.':
                        pop();
                        break;
                    case '+':
                        b=pop();a=pop();
                        push(a+b);
                        break;
                    case '-':
                        b=pop();a=pop();
                        push(a-b);
                        break;
                    case '*':
                        b=pop();a=pop();
                        push(a*b);
                        break;
                    case '/':
                        b=pop();a=pop();
                        push(a/b);
                        break;
                    case ' ':
                        break;
                    case '\n':
                        if(n!=0)
                            printf("答えは%ldです。",pop());
                        n=0;
                        break;

                    default:
                        printf("不正な入力です。\n");
                        while ((c=getchar())!=EOF && c!='a')
                            ;
                        break;
                }
            }
        }


    }else if (s==1){            /*ファイルからの入力*/
        char data[100]={0};    /*訂正箇所:0クリア*/
        int ch;
        int q=1;

        fp=fopen("data.txt","r");
        if (fp==NULL){
            printf("ファイルオープン失敗\n");
            return 1;
        }

        for (int i=0;i<100;i++){
            data[i]=fgetc(fp);
            if (data[i]==EOF) break;
        }

        for (int i=0;data[i]!='\0';i++){
            if (isdigit(data[i])){
                push(data[i]);
            }else{
                switch (data[i]){
                    case '.':
                        pop();
                        break;
                    case '+':
                        b=pop();a=pop();
                        push(a+b);
                        break;
                    case '-':
                        b=pop();a=pop();
                        push(a-b);
                        break;
                    case '*':
                        b=pop();a=pop();
                        push(a*b);
                        break;
                    case '/':
                        b=pop();a=pop();
                        push(a/b);
                        break;
                    case ' ':
                        break;
                    case '\n':
                        if(n!=0)
                            printf("答えは%ldです。",pop());
                        n=0;
                        break;

                    default:
                        push(data[i]);
                        break;
                    }
                }


        }
        fclose(fp);
    }
    return 0;
}

試したこと

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

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

Xcode Version 11.2 (11B52)

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 4

+2

  • 問題点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/06 02:48

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

    キャンセル

  • 2020/06/06 03:08 編集

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

    キャンセル

  • 2020/06/06 16:54

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

    キャンセル

  • 2020/06/06 17:30 編集

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

    キャンセル

+2

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

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

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

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/06/06 16:54

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

    キャンセル

  • 2020/06/06 17:11

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

    キャンセル

  • 2020/06/06 18:41

    > いや、変えなくても、実行時に、"program < file" とすれば、fileからの読み込みとなります。

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

    キャンセル

  • 2020/06/06 22:38

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

    キャンセル

checkベストアンサー

+1

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

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

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

#include <stdio.h>   // printf, puts, scanf, fscanf, fgetc, fopen, fclose, perror
#include <stdlib.h>  // exit
#include <ctype.h>   // isdigit

typedef long ELEMENT;

#define STACK_SIZE 100

ELEMENT stack[STACK_SIZE];
int n;

void push(ELEMENT x)
{
    if (n >= STACK_SIZE) {
        puts("stack overflow");
        exit(1);
    }
    stack[n++]=x;
}

ELEMENT pop()
{
    if (n <= 0) {
        puts("stack underflow");
        exit(1);
    }
    return stack[--n];
}

int main(void)
{
    printf("数字データの時はスタックに積み、\n"
        "ピリオドの時はスタックから降ろします。(EOFで終了)\n"
        "データ入力の方法を選んでください。\n"
        "0..キーボード/1..ファイル:");
    int c;
    scanf("%d",&c);
    FILE *fp = stdin;
    if (c == 1) {
        fp = fopen("data.txt", "r");
        if (fp == NULL) { perror("data.txt"); return 1; }
    }
    while ((c = fgetc(fp)) != EOF) {
        long x;
        if (isdigit(c)) {
            ungetc(c, fp);
            fscanf(fp, "%ld", &x);
            push(x);
        } else {
            switch (c) {
            case '.': pop(); break;
            case '+': x = pop(); push(pop() + x); break;
            case '-': x = pop(); push(pop() - x); break;
            case '*': x = pop(); push(pop() * x); break;
            case '/': x = pop(); push(pop() / x); break;
            case ' ': break;
            case '\n':
                if (n > 0) printf("答えは%ldです。\n", pop());
                n = 0;
                break;
            default:
                puts("不正な入力です。");
                while ((c = fgetc(fp)) != EOF && c != 'a') ;
                break;
            }
        }
    }
    return 0;
}

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/06/06 22:10 編集

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

    キャンセル

  • 2020/06/06 22:34

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

    キャンセル

  • 2020/06/06 22:40

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

    キャンセル

+1

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

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/06/06 02:48

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

    キャンセル

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

  • ただいまの回答率 88.83%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る