• ベストアンサー

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; } 以上です.よろしくお願いします.

質問者が選んだベストアンサー

  • ベストアンサー
  • koko_u_
  • ベストアンサー率18% (459/2509)
回答No.6

私の記憶によると、関数呼び出し f(exp1, exp2) の exp1 と exp2 の評価順は不定ですが、論理積 exp1 && exp2 の評価順は「左から右へ」が保障されていたと思います。 <JIS X3010-2003 より引用> 6.5.13 論理 AND 演算子 ビット単位の2項 & 演算子とは異なり、&&演算子は左から右への評価を保障する。第1オペランドの評価の直後を副作用完了点とする。第1オペランドの値が0と比較して等しい場合、第2オペランドは評価しない。 <引用ここまで> 規格に「保障する」と書いてある以上、最適化してもその保障は保たれいているという認識ですが。誰か教えて。

hiro3322
質問者

お礼

JISで左から右は保障されていたのですか^^; う~ん.結局のところどうなんでしょう? このJIS規格でいくと,質問時の関数で問題は無いのですよね.. とりあえず,不安なので無難にブロックを作って書いたほうがいいのでしょうか.

その他の回答 (8)

  • TT414
  • ベストアンサー率18% (72/384)
回答No.9

>現在のコンパイラは、ANo.6さんが調べてくれた仕様に従ってます。 最初のCコンパイラ(K&R第1版のころのもの)から今後出る全てのCコンパイラで保証されています。 「&&」「||」「?:」「,」の四つの演算子は評価順序が決まっています。 上の四つの演算子の評価順序に依存するプログラムは山のようにあります、ですので今後改定されるかもしれないC言語の規格でも変更できないです。

hiro3322
質問者

お礼

お答え頂きありがとうございます. これで,他の開発環境でも安心してコーディングすることができます. ありがとうございました.

回答No.8

現在のコンパイラは、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)) と書くことになります。 前者のように、算術式を外に出すほうが良いと思います。

hiro3322
質問者

お礼

回答ありがとうございます. No.6さんに調べていただいた仕様に従ってりるのならば,現在のソースで問題ないですね. ありがとうございました.

回答No.7

おぉ^^ そう書いてあるなら、常にその様に動作しなければ成らないでしょう。 元の仕様がisoみたいですから、gnu系のコンパイラも同じ動作になる筈です。 暗黒時代の罠だったみたいです^^;

hiro3322
質問者

お礼

規格の話になるとよくわからないのですが, 結局のところC言語において,「(左辺 && 右辺)の評価は左辺から…」 なのでしょうか?

回答No.5

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; } と書きます。 言語仕様として{}を抜くことは出来ますが、{}を抜くのは潜在的バグになりやすいです。

hiro3322
質問者

お礼

順序は最適化されてしまうことがあるのですね. やはり,順番に依存することはダメなんですね. 以後はブロックで書きたいと思います.

  • chie65536
  • ベストアンサー率41% (2512/6032)
回答No.4

追記。 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++』の値は、インクリメントする『前』の値」だってのを忘れないように。

hiro3322
質問者

お礼

これは,メイン関数を見ると問題なく動作しています.僕の日本語が間違っているだけです.すみませんでした. この関数の仕様は引数に0を与えたときに,2が返るような関数です. なので, if(a == b){ if(count++ == num) break; } と,最終的にはなるのでしょうか.

  • chie65536
  • ベストアンサー率41% (2512/6032)
回答No.3

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言語が考案された当時の初期のコンパイラは、そんなに賢くないので「&&演算子の両辺のどれかが最初に偽だと判っても、最後まで両辺の評価を続けてしまう」と言う動作をしています。

hiro3322
質問者

お礼

&&の右辺と左辺の優先度は開発環境に依存してしまうのですね. 皆さんが仰るとおりです. 今後は,開発環境に依存しないソースを書きたいと思います.

  • koko_u_
  • ベストアンサー率18% (459/2509)
回答No.2

>よろしければ,具体的に直したら良い点等を教えてもらえませんか? if( (a == b) && (count++ == num) ) break; とは a == b のとき  count をインクリメントして num と比較  等しければ、ループを抜ける がしたいだけなのですから、そのまま書くのが良いと思います if ( a == b ) {  count++;  if ( count == num ) break; }

hiro3322
質問者

お礼

やはり,行数を減らすのではなくてしっかり書いたほうが良いのですね. ありがとうございました. 今後は気をつけたいと思います.

  • koko_u_
  • ベストアンサー率18% (459/2509)
回答No.1

>C言語の内部処理では,(a!=b)の場合,countはインクリメントされないのでしょうか? 言語仕様上 a == b が成立しないと判った段階で、それ以降の式は評価されません。すなわち count はインクリメントされません。 しかし私はこのコーディングは見難いように感じます。

hiro3322
質問者

お礼

言語仕様だったのですね.これで,開発環境を気にしないで使うことができます. ありがとうございます. コーディングが見難いですか^^; すみません.まだ未熟者でして... よろしければ,具体的に直したら良い点等を教えてもらえませんか? よろしくお願いします.

関連するQ&A

  • 配列やif文について

    課題がでました。 最大で30個の整数を入力し、それを大きい順に並び替えるプログラムを 1次元配列と繰り返し・if文を使い作成しなさい。 例は input 12 8 90 the large order 90 12 8 という課題がでました。 ここの方なら簡単とは思いますが、よろしくお願いいたします。 自分で作ったプログラムは #include <stdio.h> main() { int a[30]; int n; int imax; int work; int max ; int i,k; printf("seisuunokosuu(<=30)="); scanf("%d",&n); for(i=0;i<n;i++){ printf("input.%2d=",i+1); scanf("%d",&a[i]);} for(i=0;i<n;i++){ imax = i; max = a[imax]; for(k=i+1;k<=n;k++){ if(a[k] > max){ imax =k; max = a[imax]; } } work = a[i]; a[i] = a[imax]; a[imax] = work; } for(i=0;i<n;i++){ printf("%d\n",a[i]); } return(0); } ですが、繰り返し・if文(一度)をつかっていません。 アドバイスの方よろしくおねがいいたします。

  • Cプログラミング 

    素数を判定するプログラミングの作成で以下のプログラミングを作成したのですが、すべて 素数でないとでてしまいます。どなたか間違いをおしえていただけませんでしょうか。 #include<stdio.h> int main(void) { int num,i; printf("Please input a number:"); scanf("%d",&num); for (i=2; i < num ; i++){num % i; if (num % i == 0) break; } if (num % i == 0) printf("%d is not prime. \n" ,num); else printf("%d is prime. \n" ,num); return 0; }

  • C言語初心者です。

    #include <stdio.h> int main() { int b[100]; int i, n; int a, r, data; int count=0; printf("Please input two integers:"); fflush(0); scanf("%d %d", &a, &r); if(a<=0 || r<=1){ printf("Error\n"); } else{ for(n=0; b[n]<=80.0; n++){ if(n==0){ b[0]=0; count++; } else { for(i=0; i<=n-1; i++){ data*=r; } b[n]=a*data; printf("%d ", b[n]); count++; } } printf("\n"); for(; count>0; count--){ printf("%d ", b[count]); } } return 0; } windows8でeclipseを使ってC言語を書いてます。 eclipse上だと何もエラーが表示されてないのですが、実行し、 Please input two integers: と表示された後、適当な数字2つを入力しても何も反応しません。 稚拙な質問ですいません。どなたか原因を教えてください。

  • C言語に詳しい方、ご指導をお願いします。

    C言語を今勉強しています。 ある調べたい数値を入力して、txtの中に入っている数字(10000までの素数)を読み込み、その素数たちを利用して素因数分解しようとしています。 しかし、何度試しても読み込みだけしか行われず、 調べたい数値を入力することすらできません。 どこに問題があるのでしょうか、ぜひご指導をお願いします。 今作成しているプログラムです。 #include<stdio.h> void main(void) {int i,k,num,p[10000]; {for(i=0;scanf("%d",&p[i])!=EOF;i++){} num=i; } printf("素因数分解したい整数は?\n"); scanf("%ld",&k); printf("%ld=",k); while(p[i]<=num) { for(i=0;p[i]<=num;i++) {if((k%p[i])==0) {printf("%ld*",p[i]); k=k/p[i]; break; } } } printf("%ld\n",k); } txtの中身 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71             ・             ・             ・ 9817 9829 9833 9839 9851 9857 9859 9871 9883 9887 9901 9907 9923 9929 9931 9941 9949 9967 9973

  • C言語

    次のプログラムは、自然数を入力して,その約数を表示するものである。ブログラム中の0 ~に適するものを答えなさい。ただし,a%bはaをbで割ったときの会りを求める演算 である。 参考 約数とは、入カした自然数に対して,その数を割り切ることのできる整数である。 例えば、6の約数は、1, 2, 3,6となる。 #include <stdio.h> int main (void) int i, num; printf("自然数を入カしてください。"): scanf ("%d", &num); printf("%dの約数は", num) ; for (i =@;i <= num; i++) ( if (num % i @ 0) { printf ("%d,i@1): printf("です。 ¥n"): return 0; 9 この問題の@の部分に入る数字、記号を教えて欲しいのとなぜそうなるのかも教えていただきたいです(-。-;

  • return 1

    #include<stdio.h> int fact(int num); int main(void) { int i; printf("Input figure freely:"); scanf("%d", &i); printf("%d", fact(i)); return 0; } int fact(int num) { if(num>0){ return num * fact(num-1); }else{ return 1; } } -------------------------------------------- 上のプログラムは再帰呼び出しを使った階乗計算の プログラムです。 func()関数内のreturn 1の意味をどなたか教えて いただけないでしょうか?

  • Cプログラムの条件文  if,else if ,elseについて教えてください。

     こんにちは。課題でどうしても解らないところがあるので教えてください。  次のプログラムは一桁の数(0~9)が偶数(EVEN)か奇数(ODD)かを表示するものです。  #include <stdio.h> main() { int num; printf("please enter a number[0~9]:"); scanf("%d",&num); if (num % 2 ==0) printf("%d is an EVEN. \n", num); else printf("%d is an ODD. \n", num); } というプログラムをコンパイルして実行すると、ちゃんと実行されるのですが、10以上の数や負数を入力しても動作してしまうんです。  そこで0~9の範囲以外の数が入力されていないかを確認して、その場合を偶数、奇数の判断や表示をせず、エラーメッセージだけを表示して終了するプログラムに書き換えなくてはならないのですが、まず  (1)判断する条件式(変数numの内容が0~9の範囲にあるか)で、num >= 0 && num <= 9 をどこに入れればいいか。  (2)please enter a number[0~9]: 8 8 is an EVEN.  (←例えば8を入れると普通こうなるのですが)   please enter a number[0~9]:12   ERROR:12 is in out of range!!  (←0~9以外ののものだったら、この様にエラー表示にしたいのです。  私が考えたプログラムは  #include <stdio.h> main() { int num; printf("please enter a number[0~9]:"); scanf("%d",&num); if (num % 2 ==0) printf("%d is an EVEN. \n", num); if else printf("%d is an ODD. \n", num); else(num >= 0 && num <= 9) printf("ERROR:%d is in out of range!! \n",num); } という風に考えたのですがコンパイルできません。プログラミング初心者なので、書いている内容も解りにくいんですが、どうかよろしくお願いします。

  • if~else文の中にまたif~else文をいれるには。

     このプログラムを思うように実行したいのですが、できません。 コンパイルはできるのですが、警告が4つほど出て、結果も自分が思ってるのとは違います。 プログラムのどこを改善すべきか教えてください。 OSはWindows XPで、コンパイラはボーランドのフリーコンパイラを使用しています。 #include<stdio.h> int main(){ int a,b,c,d; printf("1か0を入力してください。\n"); scanf("%d",&a); if(a=0) { printf("2か3を入力してください。\n"); scanf("%d",&b); if(b=2){ printf("今まで合計は%dです。\n",a+b); } if(b=3){ printf("今まで合計は%dです。\n",a+b); } else { printf("指定した数字を入力して下さい。\n"); } } else if(a=1) { printf("あなたは%dを入力しました。\n",a); } else { printf("指定した数字を入力してください。\n"); } return 0; }

  • for文&if文を使った問題について教えてください。

    参考書の練習問題を解いていて、応用力がないのか理解できない ので、分かる方教えていただけませんか? 問題: キーボードから整数を入力させ、その数が素数であるかどうかを 判断するコードを記述してください。 解答: import java.io.*; class SampleP5 { public static void main(String[] args) throws IOException { System.out.println("2以上の整数を入力してください。"); BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); String str = br.readLine(); int num = Integer.parseInt(str); for(int i=2; i<=num; i++){ if(i = = num){ System.out.println(num + "は素数です。"); } else if(num % i = = 0){ System.out.println(num + "は素数ではありません。"); break; } } } } 例えば"7"を入力すると「7は素数です」と出力されるようなんですが、 『i = 7』だとして、『 if(i = = num)』の条件って当てはまるん ですか?for文でiの初期値が2だから、『2 = = 7』で当てはまらない と思うんですが・・・ 考え方が間違っているんですかね? ※ちなみに(= =)のところ、実際はスペース空いてません。  ここの画面での表示上くっついて1本の線になってしまうので、  スペースを空けて入力したまでです。

    • ベストアンサー
    • Java
  • if文について

    ソートのプログラムにおいて昇順・降順を選択して表示させるプログラムを書いてるのですが 下記のように記述するとエラーが出てしまいます。 よく調べたのですがエラー表示もよくわからないものなのでした。 どのようにすればうまく動くようになるのでしょうか? #include <stdio.h> #define swap(type, x, y) do {type t = x; x = y; y = t; } while (0) void bubble(int a[], int n) { int i, j; for (i = 0; i < n - 1; i++) { for (j = n - 1; j > i; j--) if (a[j - 1] > a[j]) swap(int, a[j - 1], a[j]); } } void bubble2(int a[], int n) { int i, j; for (i = 0; i < n - 1; i++) { for (j = n - 1; j > i; j--) if (a[j - 1] < a[j]) swap(int, a[j - 1], a[j]); } } int main(void) { int i; int x[7]; int nx = sizeof(x) / sizeof(x[0]); int select; printf("%d個の整数を入力せよ。\n", nx); for (i = 0; i < nx; i++) { printf("x[%d] : ", i); scanf("%d", &x[i]); } printf("昇順ですか降順ですか? 0:昇順/1:降順 >"); scanf("%d",&select); if (select == 0) bubble(x, nx); puts("昇順にソートしました。"); for (i = 0; i < nx; i++) printf("x[%d] = %d\n", i, x[i]); else bubble2(x, nx); puts("降順にソートしました。"); for (i = 0; i < nx; i++) printf("x[%d] = %d\n", i, x[i]); return (0); }

専門家に質問してみよう