• ベストアンサー

インクリメント演算子でのオブジェクト(変数)を評価する時期

インクリメント演算子について以下のような例題を考えてみました。 #include<stdio.h> int main(void) { int at=0; if(at++) printf("True at=%d\n",at); else printf("False at=%d\n",at); return 0; } この時答えとしては Falsue at=1です。 これは、if文の制御式( )で at=0と判定しそのごat=1となるため、else文以降が実施されるためです。 atはメモリ上に配置されると思いますが、どの時点までat=0であり、どの時点でat=1になると考えたら良いのでしょうか。 アセンブリの問題になると思いますが、宜しく願います。

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

  • ベストアンサー
  • nda23
  • ベストアンサー率54% (777/1415)
回答No.2

値を評価するまでは0、評価した後は1です。 at=at++;とすると、atの値は変化しません。 想定されるif文の翻訳(80386と仮定)は以下の通り MOV   EAX,at  ;変数からレジスタにデータを転送 INC    at     ;変数をインクリメント OR    EAX,EAX ;インクリメント前の変数の内容を評価 JZ     @xxx   ;ゼロならばELSE節へ分岐 == 以下THEN節の処理 インクリメントによってフラグが変化してしまうので、以前の値を 記録しておき、インクリメント後に評価しています。冗長にやるなら CMP   at,0    ;変数と0を比較する PUSHF        ;フラグを退避 INC    at     ;変数をインクリメント POPF        ;フラグを復旧 JZ     @xxx   ;ゼロならばELSE節へ分岐 == 以下THEN節の処理 実際に調べるには以下の通りです。 int at=0, wk; if(( at++ )||( wk = at )) { //★ = は1個だけ(= =ではない)   printf("True at=%d, wk=%d\n", at, wk); } else {   printf("False at=%d, wk=%d\n", at, wk); } 結果は "True at=1,wk=1"です。 if文の2番目の式(wk←at、wk≠0)の評価時点で、インクリメント済みに なっています。

PHYOPHYO
質問者

補足

>at=at++;とすると、atの値は変化しません。 の意味が解りません。 int at=0 at=at++; としたとき at=1に変わっているはずですが。 宜しく願います。

その他の回答 (8)

回答No.9

これは、「作用」と「評価」の混同から起こる混乱です。 解説書などには、 at++ は、「at の現在の値を評価した後、at をインクリメントする」 ++a は、「at をインクリメントした後 at の値を使う」という書き方がされている場合がありますが、これは、必ずしも正しくありません。 実は、「評価(式の値を決定する)」のと「作用を行う(インクリメントする)」というのは、独立したものです。 ですから、 at++ は、「その式が完了するまでに(というのが、つまり、「副作用完了点までに」という意味) at をインクリメントする」「式の値としては、インクリメントする前の at の値を採用する」 ++at は、「その式が完了するまでに at をインクリメントする」「式の値としては、インクリメントした後の at の値を採用する」 です。ですから、実際の at の値がいつか割るかには全く関係なく、 at++ という式の値は、インクリメントする前の at の値になるということです。 また、 at = at = at; という式ですが、 = というのは、2項演算子に分類されます。(しかも、結合順序は右から左) 従って、この式は、 at = (at = at); という式になります。 a = b = c = 0; というのは、見た目、「a と b と c に 0 を代入する」に見えますが、実際の規定は、 c に 0 を代入する。式全体( c = 0 )としての値は、 0 次に、その結果(つまり、0)を b に代入する( b = (c = 0) )この式としての値は 0 最後に、a にこの結果を代入する( a = (b = (c = 0)) )この式としての値は 0 という意味合いになります。

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.8

ついでにいえば at = at = at; も (at の値を 2度「変更」しているので) 未定義動作です. しかし at = (at = at) ? at : at; は問題なし.

  • titokani
  • ベストアンサー率19% (341/1726)
回答No.7

訂正 at=at++; これは「未定義」でした。

  • titokani
  • ベストアンサー率19% (341/1726)
回答No.6

ちなみに、 at=at++; は、副作用完了点(;)までに2回値が変わるので、結果は「不定」 if((at++)|at) これはNG。副作用完了点の前にatを参照するから。 if((at++)||at) これはOK、||は副作用完了点であるから。 で、いいと思う・・・。

  • titokani
  • ベストアンサー率19% (341/1726)
回答No.5

言語仕様で定義されています。 「副作用完了点」で検索すると、いろいろ出てきます。 この場合は、ifの()が閉じた時点。

  • nda23
  • ベストアンサー率54% (777/1415)
回答No.4

++、--はもともとPDP11系のアセンブラにあった書式です。 at = at++; は言語仕様上、全く問題ありません。 at = at = at; とか if(( at++ )||at) はやる人はいませんが、 問題なく実行可能です。 尚、最適化の度合いによっては更新時期が微妙に違うことが あります。20年くらい前のMSCにはこのバグがあって、苦労しました。

  • rinkun
  • ベストアンサー率44% (706/1571)
回答No.3

アセンブリの問題じゃなく言語仕様の問題でしょう。 at++式の値は0だけど、式の評価が終わった後ではat==1になっている。 ちなみに at=at++; は二重代入になって言語仕様的にはダメなはず。 # エラーか未定義か実装依存かはしらない if((at++)|at) とか if((at++)||at) とか も拙いんじゃないかと思う。

  • precog
  • ベストアンサー率22% (966/4314)
回答No.1

elseにjumpする前に+1されてると思います。そのほうがキャッシュが乱れないと思うので。 依存関係がないと判断されて、+1されないかもしれませんけど。(コンパイラ次第?) 論理的にはelseに入ったらもう+1されてるハズです。

関連するQ&A

  • インクリメント演算子について

    インクリメント演算子について プログラミング超初心者です。 大学のプログラミングの課題で インクリメント演算子を使って 数字をひとつずつ大きく(小さく)していくという課題が出ました 私が作ったプログラムは以下のとおりです #include <stdio.h> int main(void) { int x ; //変数宣言 printf("整数値を入力してください:"); scanf("%d",&x); printf("1ずつ増加\n"); printf("%d,",x++); printf("%d,",x++); printf("%d,",x++); printf("%d,",x++); printf("%d\n",x); printf("1ずつ減少\n"); printf("%d,",x--); printf("%d,",x--); printf("%d,",x--); printf("%d,",x--); printf("%d\n",x--); } ちなみに、整数値は10にしました。 結果は 1ずつ増加 10,11,12,13,14 1ずつ減少 14,13,12,11,10 と、望んでるものになったのですが、疑問があるので質問させていただきます。 xを変数にすると、x++はx+1になると思うのですが表示されたのは10、11、…と 10から表示されました。 しかも、増加していった最後のxはなぜ14になるのでしょうか? 課題は出来たのですが、頭が混乱しています… 詳しく解説をお願いします。

  • インクリメント演算子

    はじめまして。プログラミングについて、とても初歩的な所で つまづいています。 変数を一つだけかつ、インクリメント演算子のみを用いて 整数値を一つ入力し,その整数値を1ずつ増やした結果を出す 4,5,6,7,8  (←このようにしたい) というプログラミングを書きたいのですが、どこのサイトを見ても 5個の文字の例はありませんでした。 2個までは以下の例でできました。 #include <stdio.h> main() { int a; printf("整数値を入力"); scanf("%d",&a); printf("1ずつ増加%d,%d,\n",a++,a); } もう、この問題で何日も立ち止まっています。 お願いです、どなたか助けてください。

  • 条件演算子

    このプログラムをIF文じゃなくて条件演算子を使って書き換えることはできますか? #include <stdio.h> int main(void) { int vx; printf("整数入力しなさい:"); scanf("%d,&vx"); if(vx) puts("その数は非0");   else puts("その数は0"); return(0); }

  • C言語における複素数の四則演算について

    複素数の四則演算(a+biとc+diの四則演算)について、for文を用いて表示するプログラムについて、???の部分に何を入れたらよいかわからず、うまく実行することができません。和・差・積・商の計算種別を入れるみたいなのですが、何を入れたらいいのかわかりません。 #include <stdio.h> void fukuso(double a,double b,double c,double d,double *e,double *f,int keisan); int main(void) { double a=4, b=8, c=4, d=3, e, f; int i; for(i=1;i<5;i++){ fukuso(a,b,c,d,&e,&f,???); if(i==1) printf("和演算\n"); else if(i==2) printf("差演算\n"); else if(i==3) printf("積演算\n"); else printf("商演算\n"); printf("e=%f f=%f i\n",e,f); } return (0); } void fukuso(double a1,double b1,double a2,double b2,double *a3,double *b3,int keisan) { if(keisan==1){ *e=a+c; *f=a+c; } else if(keisan==2){ *e=a-c; *f=b-d; } else if(keisan==3){ *e=a*c-b*d; *f=a*d+c*b; } else{ *e=(a*c+b*d)/(c*c+d*d); *f=(-a*d+c*b)/(c*c+d*d); } }

  • 条件演算子でのインクリメントの使用

    マクロとして以下のような定義をします。 #define max(x,y)  ((x)>(y)?(x):(y)) この時ある本に以下のような記述がありました。 「マクロでは、max(x++,y)が、((x++)>(y)?(x++):(y))に展開されてインクリメントが2回行われるという副作用が発生します。ここに示す実現は、int型しか扱えないことが欠点とです」 とありましたが、以下のプログラム #include<stdio.h> #include"max.h"←maxマクロの定義 int main(void) { double x=1.1,y=2.0,max; max(x++,y): max=max(x,y); printf("x=%f y=%f\n",x,y); printf("max=%f\n",max); return 0; } をコンパイルすると x=2.100000 y=2.000000 max=2.100000 の結果がでます。これは 1.条件演算子ではインクリメントは1回しかおこなわれない。 2.条件演算子はdouble型でも実現できる ことになります。 この事は「インクリメントが2回行われるという副作用が発生します。ここに示す実現は、int型しか扱えないことが欠点です」という事に反する結果だとおもいますが、どこか間違いがあるのでしょうか。宜しくお願いします。環境としてはRed Hat でviを使っています。

  • インクリメントしてくれません

    配列mousの各要素を配列tousと照らし合わせ、マッチしたら変数countにインクリメントし、何回マッチングしたかを返す関数を作りました。 でも、なぜか1回しかインクリメントしてくれません。 確認のため試しにcount++部分をputf("テスト表示")に置き換えた場合マッチングした回数だけ「テスト表示」表示されました。 じゃあなぜ、変数countは一回しかインクリメントされないのでしょうか? #include <stdio.h> int DataMatch(int *, int *); void main() { int mous[6] = { 1, 15, 12, 23, 33, 42}; int tous[7] = {10, 15, 19, 23, 33, 42, 11}; int test; test = DataMatch(mous, tous); printf("%d%\n", test); } ////////////////////////////////////////////////// int DataMatch(int *buffer1, int *buffer2) { int i, j, count; for(i = 0; i < 6 ; i++ ){ for(count = 0, j = 0; j < 6; j++){ if(buffer1[i] == buffer2[j]){ count++;//インクリメントしてくれない! break; } } } return count; }

  • c言語のフローチャートについての質問です

    #include <stdio.h> int main (void) { int n; for (n=1900; n<2000; n++) { if (n%4==0 && n%100!=10) printf ("%d",n); else if(n%400==0) printf ("%d",n); } printf("\n"); return 0; } をどなたかフローチャートに直してください JIS規格のものでお願いします

  • 前置き・後置きインクリメント。

    いつも大変お世話になり誠にありがとうございます。 標記の件。 参考書を見て、考えましたがピンときません。 どなたか解りやすく説明してもらいませんでしょうか? 2種類のコードと実行は下記の通りです. test1.c #include <stdio.h> int main(void) { int a = 0; int b = 0; b = a++; printf("代入後にインクリメントしたのでbの値は%dです。\n", b); return 0; } C:\MinGW>gcc test1.c -o test1 C:\MinGW>test1.exe 代入後にインクリメントしたのでbの値は0です。 test2.c #include <stdio.h> int main(void) { int a = 0; int b = 0; b = ++a; printf("代入前にインクリメントしたのでbの値は%dです。\n", b); return 0; } C:\MinGW>gcc test2.c -o test2 C:\MinGW>test2.exe 代入前にインクリメントしたのでbの値は1です。 前置き・後置きインクリメントの違いを論理的に ご説明願えませんでしょうか。 ご回答の程宜しくお願い申し上げます。

  • c言語についての質問です。

    #include<stdio.h> int main(void){ int a; printf("1文字たいぷしてください。\n"); scanf("%d",&a); if(a>=65 && a<=90){ printf("大文字です。\n"); } else if(a>=97 && a<=122){ printf("小文字です。\n"); } else{ printf("大文字でも小文字でもありません\n"); } return 0; } このプログラムは正しくなくて、 intをchar %dを%cにかえなければなりません。 なぜintはダメなんでしょうか? できれば丁寧に教えてください。 お願いします。

  • fopenのコード

    初歩的なことですがファイルをオープンするfopenの例題で *************************** #include<stdio.h> int main (void) {   FILE  *fp;     fp = fopen ("test1.txt","w");   if (fp==NULL) {     printf ("ファイルをオープンできませんでした。\n");     return 1;   }    else {     printf ("ファイルをオープンしました。\n");   }    fclose (fp);   printf ("ファイルをクローズしました。\n");    return  0; } *********************************というのがあります。 この中のif文の中のreturnの戻り値が1なのはなぜでしょうか? (例題によっては-1というのもありましたが・・。) またどこに戻り値を返すのでしょうか? もしファイルがオープンできなければその時点で終了になるのでしょうか?初歩的なところですがお願いします。