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

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

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

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

Q&A

解決済

6回答

3291閲覧

C言語で関数が呼ばれない

ryoutan134

総合スコア12

C

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

0グッド

2クリップ

投稿2022/04/15 11:37

C言語で税込み価格か割引価格を表示するプログラムが、思ったように動きません
苦Cの学習用C言語開発環境で動かしています。

バグのあるプログラム

C言語

1#include <stdio.h> 2#include<stdlib.h> 3int input (int alleysize ,int* write) 4 { 5 int num; 6 char inputalley[alleysize]; 7 fgets(inputalley,alleysize,stdin); 8 num = atoi(inputalley); 9 *write = num; 10 return 0; 11 } 12int main(void) 13{ 14 int anser; 15 do 16 { 17 int price = 0; 18 int mode = 0; 19 20 printf("定価を入力して下さい。\n"); 21 input(10,&price); 22 23 printf("何を示しますか。\n0 : 税込み価格\nそれ以外 : 割引価格\n"); 24 25 input(1,&mode); 26 27 if(mode = 0) 28 { 29 int tax = 0; 30 printf("税率を入力してください。\n"); 31 input(2,&tax); 32 price = (int)(price *(1+ tax/100)); 33 printf("税込み価格は%d円です。\n",price); 34 } 35 else 36 { 37 int tax = 0; 38 printf("何割引きか入力してください\n"); 39 input(2,&tax); 40 price = (int)(price * (1 - (tax/10))); 41 printf("割引後の価格は%d円です。\n",price); 42 } 43 printf("まだつずけますか?。\n1 : はい\nそれ以外 : いいえ\n"); 44 input(1,&anser); 45 }while(anser == 1); 46 return 0; 47}

実行結果

イメージ説明

一部のinput関数が呼ばれていません。

変数名などわかりにくいと思いますが回答お願いします。

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

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

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

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

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

thkana

2022/04/15 12:01

プログラムに全然関係ないですけれど、どうにも気持ち悪いので。 「続ける」はひらがなでは「つづける」と書きます。
jimbe

2022/04/15 13:15

fgets へのパラメータを確認してください。
episteme

2022/04/15 13:54

プログラムに全然関係ないですけれど、どうにも気持ち悪いので。 alley じゃなく array です。
BeatStar

2022/04/15 20:59

基本も全くできていないし、ロジックもめちゃくちゃ。 問題を解く前に基礎からやった方が早いかも。
Zuishin

2022/04/16 00:00

プログラムに全然関係ないですけれど、どうにも気持ち悪いので。 anser じゃなく answer です。
BeatStar

2022/04/16 03:49

後、実行結果に関してもできるだけ画像はやめましょう。GUIとかの外観に関してとかのように文字では表現しづらい部分だけ画像にすべきです。(HTMLでのデザインがずれる問題とか) コピペで実行結果を張り付ければ他のユーザ(回答者とか)がテキストエディタとかに張り付けて参考にするとかも出来ますが、画像だとそれが出来ません。手打ちでやればできますが面倒です。 質問するなら読み手に配慮する意識をしましょう。(結果的に出来ていなくても改善はできるので)
guest

回答6

0

ベストアンサー

(※ 現在修正中です…)

[指摘]

関係ない部分ですが気になった部分を書きます。

まず一つ目。変数名 等はちゃんと名前を付けるべきです。
趣味で自分ひとりで開発する場合でも保守は当たり前のようにやります。一か月前のコードに手を入れるとか当たり前のことです。一か月前の自分はもはや他人です。自分が組んだコードですらマジで忘れています。読めばわかるとは思いますが、変数名がでたらめだとわかりにくくなります。ましてや実際に会社とかで開発する際は複数人で開発するのが当たり前のようなので、わかるわけないです。ちゃんと変数名を定義しましょう。

inputalley[alleysize];

→ inputarray[arraysize]では?

二つ目。記号の意味を明確に理解すべきです。

if(mode = 0)となっていますが、C言語やC++においてはif文内の条件式で 「=演算子」は「単なる代入」です。つまり、「modeの値が0なら」ではなく「modeに0を代入できるなら」的な意味になってしまいます。
そうなるとtrueになるのでelseとかに行きません。

三つ目。関数をちゃんと理解しましょう。引数や戻り値の理解がまったく出来ていません。

input関数を見ると、int input(int alleysize ,int* write)となっていますが、それぞれの引数・戻り値はどういう意味でしょうか? 引数 alleysizeの役割は? 引数 writeの役割は? input関数の戻り値であるintの役割は?

input関数の定義を見ると、

C

1int input (int alleysize ,int* write) 2{ 3 int num; 4 char inputalley[alleysize]; 5 fgets(inputalley,alleysize,stdin); 6 num = atoi(inputalley); 7 *write = num; 8 return 0; 9}

となっていますが、ちょっと違和感があります。

まず、なぜ第一引数alleysizeが必要なのでしょうか? 処理内容を考えると別に必要ないのでは?
コードを読んでみましょう。
コードを読むコツは「一行レベルで、その行が何をしているかを考えながら読む」です。

C

1int input (int alleysize ,int* write) 2{ 3 4 int num; 5 char inputalley[alleysize]; 6 fgets(inputalley,alleysize,stdin); 7 num = atoi(inputalley); 8 *write = num; 9 return 0; 10}

を読むと、

C

1int input (int alleysize ,int* write) 2{ 3 // 変数numを宣言する 4 int num; 5 // 要素数alleysizeとした配列inputalleyを宣言する 6 char inputalley[alleysize]; 7 // 標準入力からalleysize分inputalleyに入れる 8 fgets(inputalley,alleysize,stdin); 9 // inputalleyの文字列データを整数に変換してnumに入れる 10 num = atoi(inputalley); 11 // 戻り値扱いの第二引数writeにnumを設定 12 *write = num; 13 // 戻り値として0を返す 14 return 0; 15}

となる。

で、char型配列inputalleyを宣言していますが、要素数を考えてみてください。
たとえばinput( 2, &n );と呼び出したなら、inputalley[2];となるはずです。
char型配列の場合で文字列と見なすなら「終端文字」('\0'とか)が末尾につきます。
"Hello World"であれば "Hello World\0" という風に終端文字が付きますから、実際には本来の文字数 + 1 が必要になります。引数alleysizeが 2 であれば 実際の文字数は 2 - 1 = 1 つまり1文字分だけです。
(厳密には文字数ではなくバイト数ですが。)

よって

C

1// main関数内 2 3 printf("何を示しますか。\n0 : 税込み価格\nそれ以外 : 割引価格\n"); 4 5 input(1,&mode);

を読むと、要素数1 (実際に入れることができる文字数は1 - 1 = 0なので 0文字)なのでスキップされたような状態に。

さらに戻り値として return 0;と返していますがこれは意味あるのでしょうか?
仮にprintf関数とかみたいに他人が使うとしたら勘違いされますよ。
戻り値を返しているはずなのに意味のない値を返している。これならいっそvoidとして「戻り値無し」とした方がいいです。混乱のもとです。

「関数」「引数」「戻り値」これらが分かっていればありえないバグです。(厳密にはバグの温床になりやすいですが)

そして四つ目。おそらくここが原因だと考えています。「標準入出力を使う場合、バッファを意識せよ」です。

軽いサンプルとして、

C

1#include <stdio.h> 2#include<stdlib.h> 3int input (int alleysize ,int* write) 4{ 5 int num; 6 char inputalley[alleysize]; 7 fgets(inputalley,alleysize,stdin); 8 num = atoi(inputalley); 9 *write = num; 10 return 0; 11} 12 13int main( void ) 14{ 15 int n; 16 input( 3, &n ); 17 printf( "n = %d\n", n ); 18 int m; 19 input( 3, &m ); 20 printf( "m = %d\n", m ); 21 int p; 22 input( 3, &p ); 23 printf( "p = %d\n", p ); 24return 0; 25}

とやってみたところ、nとpは入力を促されるが、mだけはなぜかスキップされる。(nを入力直後に"n = ..."と"m = ..."が同時に表示される)

C++でも一応この問題があって、cin >> 変数; の直後にstd::getline( ... );を使うとスキップされるという現象があります。

ユーザがコンソールに入力する。するとコンソールに文字列なり数値なりのデータが記録され、そこからNバイト分読み取っているようです。たとえば10バイト分入力したとしてfgets関数とかで4バイト分取り出した場合、コンソールには 10 - 4 = 6バイト分データがまだ残っています。

この残ったデータがあるので二回目のfgets関数が取り出すときに残りのデータを『新しく入力されたもの』と解釈して読み取る。そうするとユーザは入力していないのになぜか「入力された」と解釈していますからスキップされたような状態になります。

恐らくこれが原因かと。

「C言語 標準入力 スキップされる」で検索すると質問: ch = getchar();が時折スキップされます。がヒットします。

使っている関数がgetchar関数ですが発想は同じです。上記のある回答者の方の仰るようにfflush関数を使ってコンソール内のデータをフラッシュすると上手くいくのではないかと。

投稿2022/04/16 04:50

編集2022/04/16 05:04
BeatStar

総合スコア4958

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

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

usonko

2022/05/01 20:15 編集

違います。代入は代入した値に評価されます。mode = 0 なら 0, mode = -7 なら -7と評価されます。 >if(mode = 0)となっていますが、C言語やC++においてはif文内の条件式で 「=演算子」は「単なる代入」です。つまり、「modeの値が0なら」ではなく「modeに0を代入できるなら」的な意味になってしまいます。
guest

0

(追記依頼にしようかとも思ったけれど...)

C

1int input (int alleysize ,int* write) 2 { 3 int num; 4 char inputalley[alleysize]; 5 printf("***func input() called.\n");//追加 6 fgets(inputalley,alleysize,stdin); 7 num = atoi(inputalley); 8 *write = num; 9 return 0; 10 }

として実行してみて、それでも

関数が呼ばれない

という現象なのかどうか考えてみてください。

そして、input()関数はどういう関数でしょう。説明してみてください。その点で勘違いしていないか...という気がします。

投稿2022/04/15 12:00

thkana

総合スコア7639

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

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

ryoutan134

2022/04/16 00:23

実行してみると、関数自体は呼ばれていました、入力が何か間違っているということしょうか? また、input関数は、入力された数値を引数のポインタに代入する関数だと思っています。 何か間違いがありますか? 
thkana

2022/04/16 05:15

> 実行してみると、関数自体は呼ばれていました 少なくとも、検証もせずに「関数が呼ばれていない」と思い込んでしまったことを反省しましょう。 「これが原因」と思いこんでしまうことはベテランでもよくありますが、間違った思い込みをするとどんどん解決から遠ざかってしまうことになりかねません。 > 入力が何か間違っているということしょうか? そこでいう「入力」とは具体的になんのことでしょう? > 入力された数値を引数のポインタに代入する関数 ならばなぜ引数が2つあるのでしょう? 面倒なので言ってしまえば「input関数に与える引数が間違っている」のですが、 それは、input関数の設計がよろしくないことから誘発された間違いに思います。
ryoutan134

2022/05/01 11:31

引数をなくして、要素数を固定し、戻り値で値を返しましたが。結果は変わりませんでした。
thkana

2022/05/01 23:42

> 結果は変わりませんでした 適切な変更をしなかったから、だと思います input関数が中でバッファを確保しているから取得したい桁数に1を足したものを引数に... いや、そんなの考えなきゃ使えないような関数作っちゃだめでしょう。 int型って、普通のPCならたかだか符号含めて11桁。なら最初から文字バッファのサイズは12(終端文字含む)にしてしまえばよい。それをいちいち指定するメリットがどこかにありますか? 不要な引数を指定しなきゃ動かない->悪い設計です なんらかの理由で桁数を制限したいなら、「数値を扱う関数」なのですから「取得したい桁数」を指定するようにすべきです。バッファのサイズ云々ってのは関数の側の内部処理に基づく身勝手な都合なのだから、それを関数の利用者に押し付けるべきではありません。自分自身ですらこのように間違ってしまうのですから... 意味のわかりにくい引数を指定させる ->悪い設計です > 引数をなくして 合理的な理由で、明確な指針のもとに引数を要求するのは何ら悪いことではありません。なくせばいいというものではないです。
guest

0

基本的にfgetsの使い方に問題があるようです。
1回の入力ではEnterキーが押下されるまでを読み取るようにするのが普通です。
とりあえず、動くようにしました。
//修正 のコメントがある箇所が、修正場所です。

C

1#include <stdio.h> 2#include<stdlib.h> 3int input (int alleysize ,int* write) 4 { 5 int num; 6 //char inputalley[alleysize]; 7 char inputalley[256]; //修正 8 fgets(inputalley,256,stdin); //修正 9 num = atoi(inputalley); 10 *write = num; 11 return 0; 12 } 13int main(void) 14{ 15 int anser; 16 do 17 { 18 int price = 0; 19 int mode = 0; 20 21 printf("定価を入力して下さい。\n"); 22 input(10,&price); 23 24 printf("何を示しますか。\n0 : 税込み価格\nそれ以外 : 割引価格\n"); 25 26 input(1,&mode); 27 28 if(mode == 0) //修正 29 { 30 int tax = 0; 31 printf("税率を入力してください。\n"); 32 input(2,&tax); 33 price = (int)(price *(1+ tax/100)); 34 printf("税込み価格は%d円です。\n",price); 35 } 36 else 37 { 38 int tax = 0; 39 printf("何割引きか入力してください\n"); 40 input(2,&tax); 41 price = (int)(price * (1 - (tax/10))); 42 printf("割引後の価格は%d円です。\n",price); 43 } 44 printf("まだつずけますか?。\n1 : はい\nそれ以外 : いいえ\n"); 45 input(1,&anser); 46 }while(anser == 1); 47 return 0; 48} 49

投稿2022/04/16 01:23

tatsu99

総合スコア5438

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

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

0

if(mode = 0)

ここ、代入文なので、modeは、常に 0
従って、 false となり、 elseの方に行きませんか?

ぱっと、目についたところ。

コンパイル環境が分かりませんが、最近のだと、警告が出ないか?

投稿2022/04/15 12:02

pepperleaf

総合スコア6383

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

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

0

✕ つずける
○ つづける

誰もツッコんでないみたいなので一応w

「続ける」に変換できるかどうかで判断するのがひとつの手です。
teratailっぽいことを言うなら、コンパイラに文法ミスをチェックしてもらうのと同じことですね。

参考:なぜか変換できないとは (ナゼカヘンカンデキナイとは) [単語記事] - ニコニコ大百科 https://dic.nicovideo.jp/a/%E3%81%AA%E3%81%9C%E3%81%8B%E5%A4%89%E6%8F%9B%E3%81%A7%E3%81%8D%E3%81%AA%E3%81%84

array→alleyという間違いもそうですが、誤字が多いとそれだけ他のプログラマに伝わりにくくなったり、検索でヒットしなくなったりして保守性が下がりますし、何より「バカだと思われる」という、貴方のキャリアにとって重大なリスクが生じます。なのでなるべく直していきましょう。

投稿2022/04/19 00:50

rabbitman

総合スコア37

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

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

ryoutan134

2022/05/01 11:29

ありがとうございます。 気を付けます。
guest

0

fgetsの仕様は「第2引数の数-1文字を読み取る」なので、

c

1input(1,&mode);

で呼ばれた際、第2引数に1がわたるため1文字も読み取られず、modeが空っぽになります。

c

1char inputalley[alleysize+1]; 2fgets(inputalley,alleysize+1,stdin);

として+1しておくか、そもそもinput関数の引数で配列サイズを決めるのではなく余裕を持った数にしておく

c

1char inputalley[20]; 2fgets(inputalley,20,stdin);

などかと思います。ただしfgetsの仕様では改行文字も配列に格納されるようなのでその辺は注意が必要かもしれません(atoi関数がよろしくやってくれていそうですが)。

あと mode=0 は mode==0 で、 tax/100 は tax/100.0f などとしないと期待する結果は出なさそうです。

投稿2022/04/15 22:15

h-okhs

総合スコア149

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

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

ryoutan134

2022/04/16 00:23

前者の方法でやってみたものの結果は変わりませんでした。 また、バグと条件式は関係がないようです。
usonko

2022/04/19 08:24

input(1,&mode); の後ろに printf("%d\n", mode); を追加してmodeの値を表示(またはデバッガで変数の値を見る)し、回答者さんの変更の前後で、関数input()の動作がどう変わったかを確認してみることをお勧めします。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問