プログラミング初心者です
自然数の四則演算のプログラムを作ったところ
コンパイル時にはエラーは起こりませんでしたが実行時にエラーが起こり、動作が停止してしまいました。
自分で原因が特定できなかったのでこちらを頼らせてもらうことにしました。
ご教授お願いします。
<環境>
Windows 7 Home Premium 64-bit
Visual Studio 2013
#include<stdio.h>
#include<ctype.h>
#include<stdlib.h>
int main( void )
{
char str[2][128]={{0}};
int i,j,err=0,cont=0,wari=0;
double c,d,dans;
long a,b,lans;
puts("自然数の四則演算('+''-''*''/')を計算");
//被加数,被減数,被乗数,被除数と演算の判定
i=0;
while((str[0][i]=getchar())!='\n'){
if(str[0][0]=='+' || str[0][0]=='*' || str[0][0]=='/' || str[0][0]=='-'){
err=1;
}
if(str[0][i]=='+' || str[0][i]=='*' || str[0][i]=='/' || str[0][i]=='-'){
cont=1; //2つ目の数字を読み込むか否かの判定
break;
}else if(isdigit(str[0][i])==0){
err=1; //数字と演算方法以外の入力はエラー
}
i++;
}
//加数,減数,乗数,除数の判定
if(cont==1 && err!=1){
j=0;
while(str[1][j]!='\n'){
if(isdigit(str[0][i])==0){
err=1; //数字以外の入力があればエラー
}
j++;
}
str[1][j]='\0';
if(err==1){
printf("エラー入力");
return 0;
}
switch(str[0][i]){
case '+':
str[0][i]='\0';
a=atol(str[0]);
b=atol(str[1]);
lans=a+b;
break;
case '-':
str[0][i]='\0';
a=atol(str[0]);
b=atol(str[1]);
lans=a-b;
break;
case '*':
str[0][i]='\0';
a=atol(str[0]);
b=atol(str[1]);
lans=a*b;
break;
case '/':
str[0][i]='\0';
c=atol(str[0]);
d=atol(str[1]);
dans=(double)c/d;
wari=1; //割り算の判定
break;
default:
break;
}
}
if(err==0){
if(wari==0){
printf("計算結果:%ld\n",lans);
}else if(wari==1){
printf("計算結果:%f\n",dans);
}
}else{
printf("エラー入力です\n");
}
return 0;
}
No.3ベストアンサー
- 回答日時:
もう少しちゃんと見てみましたが、やはり不思議な仕様のコードですね。
> //被加数,被減数,被乗数,被除数と演算の判定
> i=0;
> while((str[0][i]=getchar())!='\n'){
と書いているので'\n'で入力が終了することを期待しているのでしょうが、実際に四則演算を入力しようとすると、次のところでループから脱出します。
> if(str[0][i]=='+' || str[0][i]=='*' || str[0][i]=='/' || str[0][i]=='-'){
> cont=1; //2つ目の数字を読み込むか否かの判定
> break;
例えば、"1+2"という入力をしたら、"1+"(ただし、NULL文字では終わっているとは限らない)がstr[0]に入った状態でループを出ます。
その後、次のコードを実行します。
> //加数,減数,乗数,除数の判定
> if(cont==1 && err!=1){
> j=0;
> while(str[1][j]!='\n'){
なぜか、str[1]となっているので、中に何が入っているか不明なところを\nが出てくるまで探し続けます。
> if(isdigit(str[0][i])==0){
> err=1; //数字以外の入力があればエラー
> }
> j++;
> }
と、数値以外が来てもお構いなしにstr[1]の中身を読み続け、str[1]に運良く'\n'が入っていない限り、OSがメモリーへのアクセス違反を検出して強制終了となるまでメモリーを読み続けるでしょう。
ちなみに、whileの条件を while(str[1][j]!='\n'){としてもダメです。なぜなら、'\n'が入っていないから。
他にも色々と不思議なところはありますが、
多分、2項の自然数の四則演算をするプログラムだと思ったので、質問文にあるコードを元に、プログラムを読みにくくするところを省いて多分やりたいことだろうというコードに書き換えてみました。
#include<stdio.h>
#include<ctype.h>
#include<stdlib.h>
int
main(void)
{
char str [128] = {0};
int i , j, err = 0, op_pos = 0, wari = 0;
double dans;
long a , b, lans;
puts("自然数の四則演算('+''-''*''/')を計算");
//被加数, 被減数, 被乗数, 被除数と演算の判定
i = 0;
while ((str[i] = getchar()) != '\n') {
if (str[0] == '+' || str[0] == '*' || str[0] == '/' || str[0] == '-') {
err = 1;
}
if (str[i] == '+' || str[i] == '*' || str[i] == '/' || str[i] == '-') {
//オペレーターの位置を保存
if (op_pos != 0) {
err = 1;
break;
}
op_pos = i;
} else if (!isdigit(str[i])) {
err = 1;
//数字と演算方法以外の入力はエラー
}
i++;
}
if (err == 1) {
printf("エラー入力");
return 0;
}
a = atol(&str[0]);
b = atol(&str[op_pos + 1]);
switch (str[op_pos]) {
case '+':
lans = a + b;
break;
case '-':
lans = a - b;
break;
case '*':
lans = a * b;
break;
case '/':
if (b == 0) {
// devided by 0.
err = 1;
} else {
dans = (double)(a) / (double)(b);
wari = 1;
}
break;
default:
break;
}
if (err == 0) {
if (wari == 0) {
printf("計算結果:%ld\n", lans);
} else if (wari == 1) {
printf("計算結果:%f\n", dans);
}
} else {
printf("エラー入力です\n");
}
return 0;
}
今後、1+2*3に7と答えるようにするなら、字句解析や演算子順序解析に付いて勉強したほうがよいかもしれません。
ご回答ありがとうございます。
多々ツッコミたいところがあると存じますが自分の力不足であります。大変申し訳ありません。
> //被加数,被減数,被乗数,被除数と演算の判定
> i=0;
> while((str[0][i]=getchar())!='\n'){
この部分に関しまして算術演算子が入力されない場合を考慮して書きました。
> //加数,減数,乗数,除数の判定
> if(cont==1 && err!=1){
> j=0;
> while(str[1][j]!='\n'){
この部分も改めて見直してみると確かにおかしく感じました。
自分の見落としです。
while((str[1][j]=getchar())!='\n')
このように書いた気になっていました。
申し訳ありません。
そうです!私がやりたかったことは書き換えていただいたコードのようなプログラムです。
私のつたない文章と見るにも耐えないコードで書き換えていただいて本当にありがとうございました。
後々やりたく思っていましたの字句解析や演算子順序解析についても勉強していきたいと思います。
No.5
- 回答日時:
演算子順位構文解析ですね。
演算子の優先順位に基づいた構文解析なので。なんで順序と書いたんだろう。
>>char str[2][128]={{0}};
>で、全域0x00でクリアされていますので。
たしかにそうですね。
あと、入力でバッファを溢れさせられるというのも気になりますよね。
No.4
- 回答日時:
>例えば、"1+2"という入力をしたら、"1+"(ただし、NULL文字では終わっているとは限らない)がstr[0]に入った状態でループを出ます。
'\0'ターミネートは…おそらく保証されていると考えよいかと思われます。
>char str[2][128]={{0}};
で、全域0x00でクリアされていますので。
よって…
>なぜか、str[1]となっているので、中に何が入っているか不明なところを\nが出てくるまで探し続けます。
で、バッファオーバーランも保証されていますね。
str[1]には0x00しかないのですから。
その結果が、
>・123*aiueo
>のような入力をしましたところ
>・(省略).exeは動作を停止しました
>問題が発生したため,プログラムが正しく動作しなくなりました。プログラムは閉じられ、解決策がある場合はWindowsから通知されます。
なのでしょう。
# str[1]にもstr[0]の時の入力があると読み違えてました。
# まぁどちらにしろ正しく動作しないのですが。
No.2
- 回答日時:
動作確認時のネタとしてひとつ。
・最初の文字で「+」「-」「/」「*」のいずれかを入力したときにどうしたい?
で、現状のコードで何が起こる?
・最初の入力で「a」「Backspace」「b」「Enter」とした場合にどうなる?
str[0][]に入っている文字列はどうなってる?
iの値はそれぞれの入力後にどうなってる?
・最初の入力で「a」「1」「/」とした場合にどうなる?
・最初の入力で「1」「z」「/」として、次に「a」「Enter」とした場合にどうなる?
などなど。
デバッガ使ってステップ実行やらしてみることを勧めますよ。
# printf()デバッグでもいいんだけど…標準入力使っている場合ちょっと…ね。
No.1
- 回答日時:
>コンパイル時にはエラーは起こりませんでしたが実行時にエラーが起こり、動作が停止してしまいました。
>自分で原因が特定できなかったのでこちらを頼らせてもらうことにしました。
どういう入力を行い、どんなエラーが発生して、動作が停止したと判断した根拠はなんです?
軽くしか見ていませんが、不思議な仕様のコードですねぇ。
・入力エラーがあってもその文字は有効とする。
->のでatol()は0(変換不可でエラー)とする。
・2つ目の数字入力は数字かどうかは関係ない。
->ので、やっぱりatol()は0を返す。
・除算だったらOSが止めてくれる。
この回答への補足
解答ありがとうございます。
>どういう入力を行い、どんなエラーが発生して、動作が停止したと判断した根拠はなんです?
大変申し訳ありません。私の説明不足です。
terapadで作成した拡張子[.c]のファイルを開発者コマンドプロンプトfor VS2013 にてコンパイルしexeファイルを実行した際にキーボードから
・123+123
・123*aiueo
のような入力をしましたところ
・(省略).exeは動作を停止しました
問題が発生したため,プログラムが正しく動作しなくなりました。プログラムは閉じられ、解決策がある場合はWindowsから通知されます。
上記のように表示されましたので 動作が停止したと判断しました。
>入力エラーあってもその文字は有効とする。
>2つ目の数字入力は数字かどうかは関係ない。
に関しましては
if(err==1){
printf("エラー入力");
return 0;
}
この記述でプログラムを終了することができると私が思っているので気にしていませんでした。
>除算だったらOSが止めてくれる。
また見返しましたところ分数において分母が0の場合を考えておりませんでした。申し訳ありません
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
関連するカテゴリからQ&Aを探す
おすすめ情報
- ・漫画をレンタルでお得に読める!
- ・街中で見かけて「グッときた人」の思い出
- ・「一気に最後まで読んだ」本、教えて下さい!
- ・幼稚園時代「何組」でしたか?
- ・激凹みから立ち直る方法
- ・1つだけ過去を変えられるとしたら?
- ・【あるあるbot連動企画】あるあるbotに投稿したけど採用されなかったあるある募集
- ・【あるあるbot連動企画】フォロワー20万人のアカウントであなたのあるあるを披露してみませんか?
- ・映画のエンドロール観る派?観ない派?
- ・海外旅行から帰ってきたら、まず何を食べる?
- ・誕生日にもらった意外なもの
- ・天使と悪魔選手権
- ・ちょっと先の未来クイズ第2問
- ・【大喜利】【投稿~9/7】 ロボットの住む世界で流行ってる罰ゲームとは?
- ・推しミネラルウォーターはありますか?
- ・都道府県穴埋めゲーム
- ・この人頭いいなと思ったエピソード
- ・準・究極の選択
- ・ゆるやかでぃべーと タイムマシンを破壊すべきか。
- ・歩いた自慢大会
- ・許せない心理テスト
- ・字面がカッコいい英単語
- ・これ何て呼びますか Part2
- ・人生で一番思い出に残ってる靴
- ・ゆるやかでぃべーと すべての高校生はアルバイトをするべきだ。
- ・初めて自分の家と他人の家が違う、と意識した時
- ・単二電池
- ・チョコミントアイス
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
java初心者です。入力されたの...
-
入力候補を表示させるには・・・?
-
入力する数字のサイズ制限について
-
C++:cinが上手く使えない
-
文字のリアルタイム検索がした...
-
プログラミング初心者です。 Py...
-
キーボード入力によるループの終了
-
c言語応用で血液型判定プログ...
-
数字以外が入力されたらエラー...
-
VBAで月末日の求め方について
-
*をユーザーが入力した数字の数...
-
java プログラム 繰り返しにつ...
-
fopenで強制終了
-
batプログラム上で文字列を入力...
-
キーボードを押すまで処理を中...
-
gets_sがうまく動かない
-
標準入力でEnter入力でも改行さ...
-
scanfが2回使えない・・・?;
-
rubyで標準入力とファイルから...
-
Eclipseコンソール表示を、リセ...
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
正負を反転させて出力するプロ...
-
プログラミング初心者です。 Py...
-
数字以外が入力されたらエラー...
-
Eclipseコンソール表示を、リセ...
-
Excel VBAで、Application.Inpu...
-
*をユーザーが入力した数字の数...
-
java初心者です。入力されたの...
-
Linuxで入力待ちなしkeyread関...
-
batプログラム上で文字列を入力...
-
WindowsでEOF
-
Userformの入力順序をタブオー...
-
コマンドプロンプトからのEOFの...
-
EDITコントロールで入力できる...
-
VisualStudio2019のコードアナ...
-
電卓の小数点
-
Eclipseでコマンドラインを入力...
-
小数か整数かを判定する方法
-
cout関数を使っているのですが...
-
UWSCで変数をキー入力
-
ワードで文字を入力する時の変...
おすすめ情報