- ベストアンサー
if文の論理積について
if文の論理積についての質問があります. 以下の関数は(int)num番目の素数を戻り値とする関数です. long sosuu(int num) { long a,b; int count = 0; for(a = 2; ; a++){ for(b = 2; b < a; b++){ if(a % b ==0) break; } if( (a == b) && (count++ == num)) break; /*ここが問題*/ } return a; } 今のところ,この関数で問題はおきていませんが, if((a==b)&&(count++ == num)); についての質問です. この場合,C言語の内部処理では,(a!=b)の場合,countはインクリメントされないのでしょうか? 論理積で左辺が0の場合は右辺の処理も行われないのでしょうか? 僕が使っている開発環境(BorlandC++,VC++)では両方とも問題は無かったのですが,C言語業界全体としてはどうなのでしょう? 僕としてはソースが見やすくなって都合が良いのですが,他の開発環境担になったとき,バグを出すことになりそうで少し不安です. 他の開発環境を持っている方,教えてください. この関数を使うメイン関数も置いておくので,良かったら,チェック用として使ってください. /*プログラムエントリポイント*/ int main(int num,char *main_hikisu[]) { long input,i,count,sosu; for(i = 1; i < num ; i++) { /*文字列から数字を取得*/ input = atoi(main_hikisu[i]); /*素因数分解できない形の取得*/ if(input <= 1){ printf("%sは素因数分解できません\n",main_hikisu[i]); continue; } /*素因数分解開始*/ printf("%d = ",input); count = 0; do{ sosu = sosuu(count); if(input % sosu == 0){ input /= sosu; printf("%d",sosu); if(input != 1) printf("*"); }else count++; }while( input != 1); printf("\n"); } if(num == 1){ printf("このプログラムはコマンドライン引数に数字を入れることにより,素因数分解を実行します"); }else{ printf("プログラム終了\a"); } return 0; } 以上です.よろしくお願いします.
- みんなの回答 (9)
- 専門家の回答
質問者が選んだベストアンサー
私の記憶によると、関数呼び出し f(exp1, exp2) の exp1 と exp2 の評価順は不定ですが、論理積 exp1 && exp2 の評価順は「左から右へ」が保障されていたと思います。 <JIS X3010-2003 より引用> 6.5.13 論理 AND 演算子 ビット単位の2項 & 演算子とは異なり、&&演算子は左から右への評価を保障する。第1オペランドの評価の直後を副作用完了点とする。第1オペランドの値が0と比較して等しい場合、第2オペランドは評価しない。 <引用ここまで> 規格に「保障する」と書いてある以上、最適化してもその保障は保たれいているという認識ですが。誰か教えて。
その他の回答 (8)
- TT414
- ベストアンサー率18% (72/384)
>現在のコンパイラは、ANo.6さんが調べてくれた仕様に従ってます。 最初のCコンパイラ(K&R第1版のころのもの)から今後出る全てのCコンパイラで保証されています。 「&&」「||」「?:」「,」の四つの演算子は評価順序が決まっています。 上の四つの演算子の評価順序に依存するプログラムは山のようにあります、ですので今後改定されるかもしれないC言語の規格でも変更できないです。
お礼
お答え頂きありがとうございます. これで,他の開発環境でも安心してコーディングすることができます. ありがとうございました.
- nuko_punch
- ベストアンサー率40% (10/25)
現在のコンパイラは、ANo.6さんが調べてくれた仕様に従ってます。 >if((a==b)&&(count++ == num)) 「if((a==b)&&(++count == num))」って指摘があるようなので 「a == b」が偽だった場合、「++count == num」は評価されないとあり、「a == b」副作用完了点とあります。 評価もされないし、副作用もないのなら、++countは実行されないでしょう。 確実インクリメントさせたい場合は count ++; if ((a == b) && (count == num)) か if ((++ count == num) && (a == b)) と書くことになります。 前者のように、算術式を外に出すほうが良いと思います。
お礼
回答ありがとうございます. No.6さんに調べていただいた仕様に従ってりるのならば,現在のソースで問題ないですね. ありがとうございました.
- nuko_punch
- ベストアンサー率40% (10/25)
おぉ^^ そう書いてあるなら、常にその様に動作しなければ成らないでしょう。 元の仕様がisoみたいですから、gnu系のコンパイラも同じ動作になる筈です。 暗黒時代の罠だったみたいです^^;
お礼
規格の話になるとよくわからないのですが, 結局のところC言語において,「(左辺 && 右辺)の評価は左辺から…」 なのでしょうか?
- nuko_punch
- ベストアンサー率40% (10/25)
Cの条件文などの評価順は保障されていません。 コンパイラーによります。 よって、順序に依存したコードを記述するのはNGです。 以下の文などはNULLポインタ例外が発生する可能性があります。 if (ptr != NULL && *ptr != '\0') { } 例えば num ++; if (num == 2 || num2 == 0) { } などの場合、CPUによってはnum2を先に評価した方が(機械語で)高速なコードが出来ます。 最適化などを行った場合、num2の方が先に評価される可能性があります。 関係ないですが。 私の場合 if(a % b ==0) break; は if(a % b ==0) { break; } と書きます。 言語仕様として{}を抜くことは出来ますが、{}を抜くのは潜在的バグになりやすいです。
お礼
順序は最適化されてしまうことがあるのですね. やはり,順番に依存することはダメなんですね. 以後はブロックで書きたいと思います.
- chie65536
- ベストアンサー率41% (2512/6032)
追記。 if ( a == b ) { count++; if ( count == num ) break; } と同じ意味で if( (a == b) && (count++ == num)) break; と書いてるなら、&&演算子がどうこう言う前にバグってる。 1個目の素数が欲しい時(numを1にした時)、aとbが一致して、インクリメント前のcountとnumが一致するのは、1個目の素数が見付かった時ではなく、2個目の素数が見付かった時。 countは0で初期化してるので「インクリメントした後で、numと等しいかどうか」をチェックしないとならない。なので if( (a == b) && (++count == num)) break; と書かないとnum番目の素数は返って来ない。 「後置インクリメント『count++』の値は、インクリメントする『前』の値」だってのを忘れないように。
お礼
これは,メイン関数を見ると問題なく動作しています.僕の日本語が間違っているだけです.すみませんでした. この関数の仕様は引数に0を与えたときに,2が返るような関数です. なので, if(a == b){ if(count++ == num) break; } と,最終的にはなるのでしょうか.
- chie65536
- ベストアンサー率41% (2512/6032)
if( (a == b) && (count++ == num)) break; と書いた場合、言語仕様では「countの結果は不定」です。 なぜなら 「&&演算子の右辺と左辺は、どちらが先に評価されるかは処理系に依存する」 「&&演算子の右辺と左辺は、両方が常に評価されるか、それとも片方だけが評価されて偽判定された場合にもう片方が評価されないか、どちらになるかは処理系に依存する」 と言う事になっているからです。 つまり「今は動いていても、コンパイラを変えたり、他機種、他OSに移植したら、どう動くか判らない」と言う事です。 但し「どう動くか判らない」じゃ困るので「ANSI準拠のCでは、こうしましょう」と言うのがあって仕様は明確になっていますが、それは「ANSI準拠のCの世界だけでしか通用しない」ので、C言語全体で見れば、やはり「どう動くか判らない」のは変わりません。 >僕としてはソースが見やすくなって都合が良いのですが,他の開発環境担になったとき,バグを出すことになりそうで少し不安です. と言う訳で、質問者さんのソースコードは「コンパイラを変更しただけで動かなくなる可能性のある、バグったソースコードである」と言えます。 プロのCプログラマがコメントも付けずにこんなソース書いたら「上司からゲンコツ食らう」でしょう(プロ失格、ってこと) どうしてもそう書きたいなら「/* !!注!!ANSI-C以外では動かない!! */」などのコメントを付けましょう。 なお、ANo.1さんの回答の >言語仕様上 a == b が成立しないと判った段階で、それ以降の式は評価されません。 は「ANo.1さんが現在使用しているCコンパイラでは」と言う前提があります。Cコンパイラが変わったら、その保証はありません。 例えば、C言語が考案された当時の初期のコンパイラは、そんなに賢くないので「&&演算子の両辺のどれかが最初に偽だと判っても、最後まで両辺の評価を続けてしまう」と言う動作をしています。
お礼
&&の右辺と左辺の優先度は開発環境に依存してしまうのですね. 皆さんが仰るとおりです. 今後は,開発環境に依存しないソースを書きたいと思います.
- koko_u_
- ベストアンサー率18% (459/2509)
>よろしければ,具体的に直したら良い点等を教えてもらえませんか? if( (a == b) && (count++ == num) ) break; とは a == b のとき count をインクリメントして num と比較 等しければ、ループを抜ける がしたいだけなのですから、そのまま書くのが良いと思います if ( a == b ) { count++; if ( count == num ) break; }
お礼
やはり,行数を減らすのではなくてしっかり書いたほうが良いのですね. ありがとうございました. 今後は気をつけたいと思います.
- koko_u_
- ベストアンサー率18% (459/2509)
>C言語の内部処理では,(a!=b)の場合,countはインクリメントされないのでしょうか? 言語仕様上 a == b が成立しないと判った段階で、それ以降の式は評価されません。すなわち count はインクリメントされません。 しかし私はこのコーディングは見難いように感じます。
お礼
言語仕様だったのですね.これで,開発環境を気にしないで使うことができます. ありがとうございます. コーディングが見難いですか^^; すみません.まだ未熟者でして... よろしければ,具体的に直したら良い点等を教えてもらえませんか? よろしくお願いします.
お礼
JISで左から右は保障されていたのですか^^; う~ん.結局のところどうなんでしょう? このJIS規格でいくと,質問時の関数で問題は無いのですよね.. とりあえず,不安なので無難にブロックを作って書いたほうがいいのでしょうか.