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

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

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

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

Q&A

解決済

6回答

2511閲覧

コードを見てダメ出しや指摘などお願いします。

退会済みユーザー

退会済みユーザー

総合スコア0

C

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

0グッド

1クリップ

投稿2015/11/05 11:07

編集2015/11/06 08:17
#include <stdio.h> int main(void){ double a; double b; char o; //演算子 int x; //繰り返すかどうか do{ printf("電卓です。\n数字を入力してください。\n"); scanf_s("%d", &a); printf("演算子を入力してください。\n"); scanf_s("%c",&o); printf("数字を入力してください\n"); scanf_s("%d", &b); switch (o) { case +: printf("%d+%dの値は%dです", a, b, a + b); break; case -: printf("%d-%dの値は%dです", a, b, a - b); break; case *: printf("%d×%dの値は%dです", a, b, a*b); break; case /: printf("%d÷%dの値は%dです", a, b, a / b); break; case %: printf("%dと%dを割ったときの余りは%dです", a, b, a%b); break; default: printf("演算子は+か-か*か/か%のみ対応しています。"); break; } printf("\n続けますか?Yesなら偶数、Noなら奇数を入力してください。\n"); scanf_s("%d", &y); } while (y % 2 == 0); return 0; }

プログラミング自体初心者です。
電卓を作ってみました。コードをのせるので、ここをこうすると良い、この文よりこの文のほうがいい、など意見やダメ出しをお願いします。
皆さまのくださった意見や、他サイトでの指摘を元に変更してみました。
ただ、0での除算時の対応や、fgets、sscanfなど、まだ理解のできてないものもあります。

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

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

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

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

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

guest

回答6

0

ベストアンサー

そもそも、コンパイルして実行してみましたか?

  1. まず、コードブロックで投稿してください。入力エリアの上に </> というのがあると思うので、それで。
    「ここに言語を入力」のところは、「C」と書いて、「コード」のところにコードを書いてください。
  2. scanf_s の戻り値を確認していない。
  3. while (a % 2 != 1, 0)が意味不明。2で割った余りが1でないかを調べて、その結果を捨てて、0って意図は?
  4. 演算子種類を入力した後、do ~whileでループなので、必ず"1から5の間の整数で入力してください。\n"が表示される。
  5. scanf_sよりもfgetssscanf_sを組み合わせた方が、わかりやすい動作をします。
  6. while(1) { ~~~; if(~) { break; } ~~; }というループをうまく使うと良いです。

追記:
思い直して、書き直し例のプログラムを載せてみます。

C

1#include <stdio.h> 2 3int getint(char *errmsg){ 4// 整数を入力して返す 5// 整数が入力されなかったらエラーメッセージを出して入力し直し 6 char line[200]; 7 int x; 8 9 while(1) { 10 fgets(line,sizeof(line),stdin); 11 if(sscanf_s(line,"%d",&x)>0){ 12 return x; 13 } 14 printf(errmsg); 15 }; 16} 17 18int main(void){ 19 int a; //整数1 20 int b; //整数2 21 int x; //演算子 22 int y; //続けるかどうか 23 24 do{ 25 a = getint("整数のみを入力してください。\n"); 26 27 printf("演算子を入力してください。\n"); 28 printf("足し算なら1。\n引き算なら2。\n掛け算なら3。\n割り算なら4。\n余りを求めるなら5。\n"); 29 30 while(1) { 31 x = getint("1から5の間の整数で入力してください。\n"); 32 if(x>=1 && x<=5){ 33 break; 34 } 35 printf("1から5の間の整数で入力してください。\n"); 36 } 37 printf("二番目の数字を入力してください\n"); 38 39 b = getint("整数のみを入力してください。\n"); 40 41 switch (x) { 42 case 1: 43 printf("%d+%dの値は%dです", a, b, a + b); 44 break; 45 case 2: 46 printf("%d-%dの値は%dです", a, b, a - b); 47 break; 48 case 3: 49 printf("%d×%dの値は%dです", a, b, a*b); 50 break; 51 case 4: 52 if(b==0){ 53 printf("ゼロでは割れません\n"); 54 break; 55 } 56 printf("%d÷%dの値は%dです", a, b, a / b); 57 break; 58 case 5: 59 if(b==0){ 60 printf("ゼロでは割れません\n"); 61 break; 62 } 63 printf("%dと%dを割ったときの余りは%dです", a, b, a%b); 64 break; 65 } 66 printf("\n続けますか?Yesなら偶数、Noなら奇数を入力してください。\n"); 67 y = getint("Yesなら偶数、Noなら奇数を入力してください。\n"); 68 } while (y % 2 == 0); 69 return 0; 70}

細かいことを言うと、fgetsがEOFを返した場合の考慮も必要ですが、省略。

投稿2015/11/05 11:31

編集2015/11/06 23:44
otn

総合スコア84380

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

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

退会済みユーザー

退会済みユーザー

2015/11/05 11:59 編集

scanf_sの戻り値を確認とはどういうことでしょうか。 (a%2 != 1,0)は、aを2で割った余りが1か0ということを表記したかったのですが、どのようにすればよいかわからず、まず思い浮かんだ方法で表記しました。意図は整数以外が入力された時を考えたのですが、不要でしょうか。 do~whileは修正しました。 fgetsやsscanf_sはどのように使うのでしょうか。 6番目は自分で考えやってみますが、どのようにすればよいかわからなかった場合、再度質問させてもらいます。 質問など多くてすみません。回答ありがとうございます。
otn

2015/11/05 12:49

> scanf_sの戻り値を確認とはどういうことでしょうか。 scanf系は戻り値の確認が必須です。戻り値の意味については、scanf_sのリファレンスを見てください。関数を使うときは、リファレンスを見るという習慣を付けてください。 > aを2で割った余りが1か0ということを表記したかったのですが、 それは、a%2==1 || a%2==0 となりますが、2で割った余りは0か1に決まっているので、無意味ですね。 なお、, は、カンマ演算子というものです。a , b の演算結果は、aの値を捨てて、bの値となります。 捨てるというのがわかりにくければ、a,b は a*0+b と思っても良い。 > 意図は整数以外が入力された時を考えたのですが、 整数以外が入力されたときの、scanf_s("%d",&a)の結果はどうなると思ったのでしょうか? で、実際にやった結果はどうなりましたか? > fgetsやsscanf_sはどのように使うのでしょうか。 fgetsやsscanf_sの働きを調べた上での質問でしょうか? まあ、フォーマットに合わない入力をしたときのscanf_sの動作に不満が出てくるまではscanf_sでも良いです。
退会済みユーザー

退会済みユーザー

2015/11/06 08:56

わかりました。 リファレンスというもの自体を知りませんでした。これからわからないものは確認していきます。 aを2で割った~~は小数などを入力された時などを考えたのですが、変数を実数に対応できるものに変えました。 整数以外が入力されたときは、whileのprintf文を無限ループしていました。 fgetsやsscanfをしらべてみたのですが、自分の知識では理解ができませんでした。すみません
otn

2015/11/06 10:26

> 小数などを入力された時などを考えたのですが、 scanf("%d",~)に対して、1.5とかの実数を入力した場合にどうなると思っていましたか? また、scanf("%d",~)やscanf("%f",~)に対して、A とかの数字で無い物を入力したケースへの対応も必要です。
otn

2015/11/06 23:46

自分で考えてもらおうと思って具体例は伏せていましたが、思い直して、書き直し例を載せてみました。
退会済みユーザー

退会済みユーザー

2015/11/07 23:29

実数や文字が入力された時は、ifなどを使うといいでしょうか?
guest

0

{}の位置を一定にした方がいいと思いますよ
たとえば

if(){

}

if()
{

}
など混じらない方がほかの人が見るときに見やすいです

投稿2015/11/08 14:50

fujimon7111

総合スコア30

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

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

退会済みユーザー

退会済みユーザー

2015/11/08 22:42

なるほど、分かりました。後ほど時間あるときに変更します。ありがとうございます
guest

0

・ゼロ除算のガードが必要
・変数名をもっとわかりやすい名前にすべき

投稿2015/11/05 14:08

yona

総合スコア18155

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

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

退会済みユーザー

退会済みユーザー

2015/11/06 08:58

ゼロ除算のガードはどのように記述したらよいか、と変数のわかりやすい名前とは、プログラムを書く上でどのような名前にするとわかりやすく簡潔だ、というのはありますか?大抵の人は変数は、こういうものならこういう名前にする。などありましたら教えてください
yona

2015/11/06 11:29

・割る数では0を入力すると再入力を促すようにする。 ・変数の性質を捉えていて、かつ他の人も同じような名前をつけるだろうなぁって思えればいいですよ
退会済みユーザー

退会済みユーザー

2015/11/06 13:16

0で除算のやつは別の質問で直しました 変数はやはり長くなると個人的に面倒なので、次回からabcやxyzなどではなく、わかりやさいものをつかうようにします
guest

0

scanf_sの戻り値の確認を行わないと、数字以外が入力された場合に予期しない動作が発生します。

私の場合は、**続けますか?Yesなら偶数、Noなら奇数を入力してください。**の後にyesと入力すると無限ループが発生しました。

文字列が入力された場合は、scanf_s("%s")が出現するまでscanf_s()が無視されるのが原因みたいです。

投稿2015/11/06 00:57

ymknjugg

総合スコア131

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

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

退会済みユーザー

退会済みユーザー

2015/11/06 08:17

戻り値の確認はどのようにするのでしょうか
ymknjugg

2015/11/09 01:07

戻り値の確認についてはotnさんのコメントで重複する内容なので、ここでは省力します。
guest

0

  • main() にすべての処理を書かずに、適宜 関数に処理を分ける。
  • switch による処理分岐を避ける。

この2つの方針を考慮するとよいとおもいます。
質問文のコードとは大きく異なってしまいますが、↓にその方針で書いたコードを示します。

c

1// See http://www.kishiro.com/programming/c/function_pointer.html 2 3#include <stdio.h> 4 5typedef void (*FUNCPTR)(int a, int b); 6 7void op_nil(int a, int b) { 8} 9void op_add(int a, int b) { 10 printf("%d+%dの値は%dです", a, b, a + b); 11} 12void op_sub(int a, int b) { 13 printf("%d-%dの値は%dです", a, b, a - b); 14} 15void op_mul(int a, int b) { 16 printf("%d×%dの値は%dです", a, b,a* b); 17} 18void op_div(int a, int b) { 19 if (b == 0) { 20 printf("0で割ることはできません"); 21 } else { 22 printf("%d÷%dの値は%dです", a, b, a / b); 23 } 24} 25void op_mod(int a, int b) { 26 printf("%dと%dを割ったときの余りは%dです", a, b, a % b); 27} 28 29FUNCPTR operations[] = { &op_nil, &op_add, &op_sub, &op_mul, &op_div, &op_mod }; 30 31int read_quit() { 32 int q; 33 while(1) { 34 int ret; 35 printf("\n続けますか?Yesなら偶数、Noなら奇数を入力してください。\n"); 36 ret = scanf("%d", &q); 37 if (ret == 1) { 38 return (q % 2 == 0); 39 } 40 scanf("%*s"); 41 printf("整数のみを入力してください。\n"); 42 } 43 return 0; 44} 45 46void read_problem(int * a, int * b, int * kind) { 47 printf("電卓です。\n数字を入力してください。\n"); 48 while(1) { 49 int ret = scanf("%d", a); 50 if (ret == 1) { 51 break; 52 } 53 scanf("%*s"); 54 printf("整数のみを入力してください。\n"); 55 } 56 57 printf("演算子を入力してください。\n"); 58 printf("足し算なら1。\n引き算なら2。\n掛け算なら3。\n割り算なら4。\n余りを求めるなら5。\n"); 59 while(1) { 60 int ret = scanf("%d", kind); 61 if (ret == 1) { 62 if ((1 <= *kind) && (*kind <= 5)) { 63 break; 64 } 65 } else { 66 scanf("%*s"); 67 } 68 printf("1から5の間の整数で入力してください。\n"); 69 } 70 71 printf("数字を入力してください\n"); 72 while(1) { 73 int ret = scanf("%d", b); 74 if (ret == 1) { 75 break; 76 } 77 scanf("%*s"); 78 printf("整数のみを入力してください。\n"); 79 } 80} 81 82int main() { 83 int a; // 数値 84 int b; // 数値 85 int kind; // 演算の種類 86 while(1) { 87 read_problem(&a, &b, &kind); 88 operations[kind](a, b); 89 if (read_quit() == 0) { 90 break; 91 } 92 } 93}

投稿2015/11/05 22:20

katoy

総合スコア22322

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

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

Chironian

2015/11/06 02:39

横から失礼します。 > switch による処理分岐を避ける。 これは多くのプログラマーが賛同しているルールでしょうか? 関数ポインタ配列を使うよりswitch-caseの方が確実に可読性が高く、defaultを使えばバグ検出しやすいので、私自身は特に理由がない限りswitch-caseを使います。
退会済みユーザー

退会済みユーザー

2015/11/06 08:19

mainにすべての処理を~~とありますが、そうした場合の他の関数の時の処理の仕方や内容などがわからなかったり、なるべく簡潔に短いコードで作りたいです。
catsforepaw

2015/11/06 08:55

横から失礼します。 私もChironianさんと同意見で、関数ポインタのテーブル化には反対ですね。少なくともこのようなケースでは。 なせswitchによる分岐がいけないのか意見を伺いたいところではありますが。
guest

0

質問文が編集されて、記載されていたソースコードの挙動が変化したので、
それに合わせて再度、回答コードを投稿します。

電卓を発展させていくことを考えた場合、演算種類が増えていくことが予想されます。
その場合、swich 文、 if-esle では保守がしにくくなります。
そこで、あえてデータ駆動方式で書いてみています。

c

1#include <stdio.h> 2 3typedef void (*FUNCPTR)(int a, int b); 4 5void op_add(int a, int b) { 6 printf("%d+%dの値は%dです", a, b, a + b); 7} 8void op_sub(int a, int b) { 9 printf("%d-%dの値は%dです", a, b, a - b); 10} 11void op_mul(int a, int b) { 12 printf("%d×%dの値は%dです", a, b, a * b); 13} 14void op_div(int a, int b) { 15 if (b == 0) { 16 printf("0で割ることはできません"); 17 } else { 18 printf("%d÷%dの値は%dです", a, b, a / b); 19 } 20} 21void op_mod(int a, int b) { 22 printf("%dと%dを割ったときの余りは%dです", a, b, a % b); 23} 24 25struct { 26 const char op; 27 const FUNCPTR func; 28} const FUNCS[] = { 29 {'+', &op_add}, 30 {'-', &op_sub}, 31 {'*', &op_mul}, 32 {'/', &op_div}, 33 {'%', &op_mod}, 34 {'\0', NULL} // End of Data 35}; 36 37void calc(int a, int b, char op) { 38 for (int i = 0; FUNCS[i].op; i++) { 39 if (FUNCS[i].op == op) { 40 FUNCS[i].func(a, b); 41 return; 42 } 43 } 44 printf("演算子は+か-か*か/か%のみ対応しています。[%c]は不正な指定です。", op); 45} 46 47int read_quit() { 48 int q; 49 while(1) { 50 int ret; 51 printf("\n続けますか?Yesなら偶数、Noなら奇数を入力してください。\n"); 52 ret = scanf("%d", &q); 53 if (ret == 1) { 54 return (q % 2 == 0); 55 } 56 scanf("%*s"); 57 printf("整数のみを入力してください。\n"); 58 } 59 return 0; 60} 61 62void read_problem(int * a, int * b, char * kind) { 63 printf("電卓です。\n数字を入力してください。\n"); 64 while(1) { 65 int ret = scanf("%d", a); 66 if (ret == 1) { 67 break; 68 } 69 scanf("%*s"); 70 printf("整数のみを入力してください。\n"); 71 } 72 73 printf("演算子を入力してください。\n"); 74 while(1) { 75 int ret = scanf(" %c", kind); 76 if (ret == 1) { 77 break; 78 } 79 scanf("%*s"); 80 printf("演算子を入力してください。\n"); 81 } 82 83 printf("数字を入力してください\n"); 84 while(1) { 85 int ret = scanf("%d", b); 86 if (ret == 1) { 87 break; 88 } 89 scanf("%*s"); 90 printf("整数のみを入力してください。\n"); 91 } 92} 93 94int main() { 95 int a; // 数値 96 int b; // 数値 97 char op; // 演算子 98 while(1) { 99 read_problem(&a, &b, &op); 100 calc(a, b, op); 101 if (read_quit() == 0) { 102 break; 103 } 104 } 105}

投稿2015/11/06 14:02

katoy

総合スコア22322

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.51%

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

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

質問する

関連した質問