2桁以上の逆ポーランド式の計算について

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
Act

2桁以上の逆ポーランド式の計算について

#1

投稿記事 by Act » 6年前

数日前に「逆ポーランド式の計算について」というトピックを投稿させていただいたものです。
今回も学校の課題に詰まっていて、課題の提出期限も迫ってしまっているので、こちらで聞かせていただこうと思いました。

課題は、2桁以上の逆ポーランド式の計算をするプログラムを作成するというもので、数字を区別するために、数字の間のみに"|"を入力して区別するというものです。

以下が現在作成途中のプログラムになります。

コード:

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

/*外部変数*/
char stack[100];
int sp;

/*スタックポインタを初期化する関数init_stack*/
void init_stack(void)
{
  sp = -1;
}

/*スタックにデータをひとつ入れる関数push*/
void push(int a)
{
  sp += 1;
  stack[sp] = a;
}

/*スタックからデータをひとつ取り出す関数pop*/
int pop()
{
  int n;

  if(sp > -1){
    n = stack[sp];
    sp -= 1;
  }
  else{
    printf("-\n");
  }

  return n;
}

/*スタックに収納された値をすべて表示する関数show_stack*/
void show_stack()
{
  int i;

  for(i = 0;i < sp + 1 ; i++){
    printf("%d",stack[i]);
  }
}
こちらがmain関数の前までのプログラムです。

コード:

#define SIZE 100

int main(void){
  char expr[SIZE];/*計算したい逆ポーランド式を格納しておく文字列*/
  char expr2[SIZE];/*2桁の数字文字を数値に変換するときに使用する*/
  char expr3[SIZE];/*expr2[SIZE]を初期化するのに使用する*/
  int x,y,z;/*計算の時に使用する*/
  int k,i;/*ループにおいて使用する*/
  int suuti;/*計算結果*/
  int t = 0;/*2桁の文字の処理で利用*/
  char a = '+';
  char s = '-';
  char m = '*';
  char d = '/';
  char w = '|';

  init_stack();

  printf("これから作成したプログラムの動作の確認をします.\n");
  printf("計算したい逆ポーランド式を入力してください。\n");
  scanf("%s",expr);

  k = strlen(expr);
  for(i = 0;i < k; i++){
    if(expr[i] <= '9' && expr[i] >= '0'){
      expr2[t] = expr[i];
      t++;
    }
    else if(expr[i] == w){
      suuti = atoi(expr2);
      expr2[SIZE] = expr3[SIZE];/*初期化*/
      suuti = atoi(expr3);/*初期化*/
      push(suuti);
      t = 0;
      i++;
    }
    else if(expr[i] == a){
      if(t==0){
        x = pop();
        y = pop();
        z = y + x;
        push(z);
      }
      else{
      suuti = atoi(expr2);
      expr2[SIZE] = expr3[SIZE];/*初期化*/
      suuti = atoi(expr3);/*初期化*/
      push(suuti);
      x = pop();
      y = pop();
      z = y + x;
      push(z);
      t = 0;
      }
    }
    else if(expr[i] == s){
      if(t==0){
        x = pop();
        y = pop();
        z = y - x;
        push(z);
        t = 0;
      }
      else{
      suuti = atoi(expr2);
      expr2[SIZE] = expr3[SIZE];/*初期化*/
      suuti = atoi(expr3);/*初期化*/
      push(suuti);
      x = pop();
      y = pop();
      z = y - x;
      push(z);
      t = 0;
      }
    }
    else if(expr[i] == m){
      if(t==0){
        x = pop();
        y = pop();
        z = y * x;
        push(z);
      }
      else{
      suuti = atoi(expr2);
      expr2[SIZE] = expr3[SIZE];/*初期化*/
      suuti = atoi(expr3);/*初期化*/
      push(suuti);
      x = pop();
      y = pop();
      z = y * x;
      push(z);
      t = 0;
      }
    }
    else if(expr[i] == d){
      if(t==0){
        x = pop();
        y = pop();
        if(x == 0){
          printf("計算できません。(%d ÷ %d)\n",y,x);
        }
        else{
          z = y / x;
          push(z);
        }
      }
      else{
        suuti = atoi(expr2);
        expr2[SIZE] = expr3[SIZE];/*初期化*/
        suuti = atoi(expr3);/*初期化*/
        push(suuti);
        x = pop();
        y = pop();
        if(x == 0){
          printf("計算できません。(%d ÷ %d)\n",y,x);
        }
        else{
          z = y / x;
          push(z);
          t = 0;
        }
      }
    }
    else{
      printf("不正な操作です。\n");
    }
  }
  printf("入力された逆ポーランド式の計算の答えは");
  show_stack();
  printf("です。\n");

  return 0;

}
こちらがmain関数です。
動きとしては、計算したい逆ポーランド式の計算式を入力し配列exprによみこみ、その文字式の長さ分i=0からループさせます。
exprが数字なら配列expr2に格納していき、数字を分ける"|"や演算子ならそこまでのexpr2中身をスタックに積み、配列expr2を初期化する。
演算子で場合分けをした場合も、expr2が空ならすぐに計算をさせるように組みたいのですがうまくいきません。
これは考え方がそもそも間違っているのでしょうか。それとも、プログラムの組み方がおかしいのでしょうか。
課題の期限も迫っていてとても焦っています。
よろしくお願いします。

かずま

Re: 2桁以上の逆ポーランド式の計算について

#2

投稿記事 by かずま » 6年前

char expr2[SIZE], expr3[SIZE]; と宣言されたら、
expr2[0]~expr2[SIZE-1] の SIZE個の要素しか参照できません。
expr2[SIZE] = expr3[SIZE]; は範囲外アクセスです。

atoi に渡す文字列は '\0' で終了していなければなりません。
atoi(expr2) の expr2 に入っている数字列は '\0' で終了していません。
atoi呼出しの直前に expr2[t] = '\0'; が必要でしょう。
数字を見つけたら、そこで数字が続く限り、それを expr2 にため込んで
atoi を呼び出せばよいでしょう。他の演算子で処理しなくて済みます。

コード:

	for (i = 0; i < k; i++) {
		if (expr[i] <= '9' && expr[i] >= '0') {
			t = 0;
			do {
				expr2[t++] = expr[i++];
			} while (expr[i] <= '9' && expr[i] >= '0');
			expr2[t] = '\0';
			push(atoi(expr2));
			i--;
		}
		else if (expr[i] == a) push(pop() + pop());
		else if (expr[i] == s) x = pop(), push(pop() - x);
		else if (expr[i] == m) push(pop() * pop());
		else if (expr[i] == d) {
			x = pop(), y = pop();
			if (x == 0) printf("計算できません。(%d ÷ %d)\n", y, x);
			else push(y / x);
		}
		else if (expr[i] != w) printf("不正な操作です。\n");
	}
atoi の代わりに strtol を使えば、数字列の終わりの位置が分かります。
expr2 は不要です。

コード:

		if (expr[i] <= '9' && expr[i] >= '0') {
			char *p;
			push(strtol(expr + i, &p, 10));
			i = p - expr - 1;
		}

Act

Re: 2桁以上の逆ポーランド式の計算について

#3

投稿記事 by Act » 6年前

お早い返信ありがとうございます。
一つ目の方法を使って課題を進めていきたいと思います。2つ目は課題がいったん片付いてから試させていただこうと思います。

いまいちピンと来ていない部分があるのですが、とても大きい桁数の計算をするならば、それを見越してSIZEの定義値を大きくするのでは問題があるのでしょうか。
また、expr2[SIZE] = expr3[SIZE];が範囲外アクセスというのはどういうことなのでしょうか。
よろしければ教えて下さるとうれしいです。

かずま

Re: 2桁以上の逆ポーランド式の計算について

#4

投稿記事 by かずま » 6年前

Act さんが書きました:
6年前
また、expr2[SIZE] = expr3[SIZE];が範囲外アクセスというのはどういうことなのでしょうか。
expr3[SIZE] には何が入っていますか?

コード:

int a[3]; // 配列 a の要素数は 3。添字の範囲は 0~2。
a[0] = 7;
a[1] = 5;
a[2] = 3;
a[3] = 1; // やってはいけない範囲外アクセス。
atoi も strtol も使わない方法

コード:

		if (expr[i] <= '9' && expr[i] >= '0') {
			suuti = expr[i++] - '0';
			while (expr[i] <= '9' && expr[i] >= '0')
				suuti = suuti * 10 + (expr[i++] - '0');
			push(suuti);
			i--;
		}

フォーラム(掲示板)ルール
には次のように書かれています。
d. 義務行為
 "C言語何でも質問掲示板"でのみ適用される事項
 ・トピックを立て、解決した場合は「解決しました」とだけ書かず、どうやって
  解決したか他の人に分かるように書いてからトピックを終了して下さい。
解決したら、そのコードを貼り付けてください。
課題だったら、あとで入手した模範解答などを追加してもらうと、
他の皆さんの役に立つかもしれません。

Act

Re: 2桁以上の逆ポーランド式の計算について

#5

投稿記事 by Act » 6年前

expr3[SIZE]には何も入っていません。
入っていないからだめなのか、一つずつfor文で大入試手いないからダメなのでしょうか。
かずま さんが書きました:
6年前
atoi も strtol も使わない方法

コード:

		if (expr[i] <= '9' && expr[i] >= '0') {
			suuti = expr[i++] - '0';
			while (expr[i] <= '9' && expr[i] >= '0')
				suuti = suuti * 10 + (expr[i++] - '0');
			push(suuti);
			i--;
		}
ありがとうございます。

ルールについて失念していました、申し訳ありません。
ほとんどそのままですが次のようにして解決しました。細かい動作確認は明日やる予定です。

コード:


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

/*外部変数*/
int stack[100];
int sp;

/*スタックポインタを初期化する関数init_stack*/
void init_stack(void)
{
  sp = -1;
}

/*スタックにデータをひとつ入れる関数push*/
void push(int a)
{
  sp += 1;
  stack[sp] = a;
}

/*スタックからデータをひとつ取り出す関数pop*/
int pop()
{
  int n;

  if(sp > -1){
    n = stack[sp];
    sp -= 1;
  }
  else{
    printf("-\n");
  }

  return n;
}

/*スタックに収納された値をすべて表示する関数show_stack*/
void show_stack()
{
  int i;

  for(i = 0;i < sp + 1 ; i++){
    printf("%d",stack[i]);
  }
}

#define SIZE 100

int main(void){
  char expr[SIZE];/*計算したい逆ポーランド式を格納しておく文字列*/
  char expr2[SIZE];/*2桁の数字文字を数値に変換するときに使用する*/
  int x,y,z;/*計算の時に使用する*/
  int k,i;/*ループにおいて使用する*/
  int suuti;/*計算結果*/
  int t = 0;/*2桁の文字の処理で利用*/
  char a = '+';
  char s = '-';
  char m = '*';
  char d = '/';
  char w = '|';

  init_stack();

  printf("これから作成したプログラムの動作の確認をします.\n");
  printf("計算したい逆ポーランド式を入力してください。\n");
  scanf("%s",expr);

  k = strlen(expr);
  for (i = 0; i < k; i++) {
    if (expr[i] <= '9' && expr[i] >= '0') {
      t = 0;
      do {
        expr2[t++] = expr[i++];
      } while (expr[i] <= '9' && expr[i] >= '0');
      expr2[t] = '\0';
      push(atoi(expr2));
      i--;
    }
    else if(expr[i] == a){
      x = pop();
      y = pop();
      z = y + x;
      push(z);
    }
    else if(expr[i] == s){
      x = pop();
      y = pop();
      z = y - x;
      push(z);
    }
    else if(expr[i] == m){
      x = pop();
      y = pop();
      z = y * x;
      push(z);
    }
    else if(expr[i] == d){
      x = pop();
      y = pop();
      if(x == 0){
        printf("計算できません。(%d ÷ %d)\n", y, x);
      }
      else
        push(y / x);
    }
    else if(expr[i] != w)
      printf("不正な操作です。\n");
  }
  printf("入力された逆ポーランド式の計算の答えは");
  show_stack();
  printf("です。\n");

  return 0;

}

返信

“C言語何でも質問掲示板” へ戻る