C言語の前置演算子と後置演算子の違いとは?

このQ&Aのポイント
  • C言語の前置演算子と後置演算子には意味の違いがあります。++aはaを1増やしてから式を評価し、a++は式を評価してからaを1増やします。
  • 前置演算子++aと後置演算子a++の結果は異なります。++aは式を評価する前にaを1増やすため、b = ++aの場合、bには増加した値が代入されます。
  • 後置演算子a++は式を評価してからaを1増やすため、b = a++の場合、bには増加する前の値が代入されます。しかし、複数の++演算子を含む式では予想と結果が異なることもあります。
回答を見る
  • ベストアンサー

C言語における前置演算子と後置演算子

C言語では ++a と a++ は違う意味を持つらしいので、実行結果がどのように変わるのか試してみました。 (1) int a = 10 ;    int b = ++a ; (2) int a = 10 ;    int b = a++ ; (1)と(2)では、aとbの値はそれぞれ次のようになりました。 (1) a: 11    b: 11 (2) a: 11    b: 10 (1)と(2)は予想通りの結果になりました。今度は次の式を試してみました。 (3) int a = 10 ;    int b = ++a + ++a ; (4) int a = 10 ;    int b = ++a + a++ ; (5) int a = 10 ;    int b = a++ + ++a ; (6) int a = 10 ;    int b = a++ + a++ ; (3)~(6)のbの値は次のように予想しました。 (3) (10+1) + (11+1) = 23 (4) (10+1) + 11 = 22 (5) 10 + (11+1) = 22 (6) 10 + 11 = 21 しかし、実際は次の値になりました。 (3) a: 12    b: 24 (4) a: 12    b: 22 (5) a: 12    b: 22 (6) a: 12    b: 20 (4)と(5)は予想通りの結果になりましたが、(3)と(6)は予想と違っていました。 どうしてこのような値になったのでしょうか?

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

  • ベストアンサー
回答No.6

ちょっと補足すると……。 入門書には、よく、 ++a は、「a のインクリメントをしてから、その値を使う」 a++ は、「a の値を使ったあとで、a をインクリメントする」 と書かれていることがあります。 実は、これは、ちょっと違うのですね。 Cでは、他の言語では文と呼んでいるものでも、(たとえば代入でも)「式」と呼ばれその値をもっています。 実は、正しくは、 ++a は、「・a をインクリメントする、・式の値は a をインクリメントしたもの」 a++ は、「・a をインクリメントする、・式の値は a の値」 となります。(どちらが先とかないのですね) そこで、「インクリメントする」のは実際にはどのタイミングかと言えば、「副作用完了点」までには終わることが保証されています。おおざっぱに言えば、; までとか、関数の括弧の中とか。 ですから、たとえば、 a++ を ・a++ が出てくるたびに、a をインクリメントする ・a++ があったら、式の評価は a を使う。副作用完了点の直前で、a をインクリメントする のどちらでも、規格には合致するわけです。 さらに、「規格では『副作用完了点までにa++は2度以上出てこない』から、その前提でコンパイラを書いているので、a++ が2回出てきたら、こける」というのも考えられます(バグではない点に注意) こういうことで、「インクリメントしてから云々」という解説も、あるいは、誤解の元かなと思います。 同じようなことは、括弧や演算子の(結合の)優先順序にも言えて、入門書で、 ・括弧の中は「先に」計算する ・乗除は加減より「先に」計算する という記述があることもあります。 これも、実は、「括弧の中は『強く』結合する」だけで、括弧の外だけを先に計算しておいて、あとから、括弧の中を計算するということも、実はあるかもしれません。

bururutti-2
質問者

お礼

なるほど、++aやa++が2回以上出てこない事を前提にして定義しているのですね。 インクリメントするタイミングまでは定められていないのですね。 回答有難うございました。

その他の回答 (6)

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

>同じコンパイラの同じバージョンで行う限り、結果は一定するでしょうが とは限りません、コンパイル時のオプション指定(最適化関係)、コンパイル時のメモリの空きサイズなどで結果は変わります。 コンパイルしてみるまでどうなるか分かりません。

bururutti-2
質問者

お礼

同じコンパイラの同じバージョンでも結果が異なる事があるのですね。 回答有難うございました。

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

うぃ, 特定の演算子 (論理演算子の &&, || とコンマ演算子) 以外ではオペランドの評価順序は定義されていませんし, 関数の実引数の評価順序も決まってません>#3. 実際には「処理系定義」じゃなくて「未定義」なので「全く同じ式であっても位置によって順序が違う」ということも許されています. Java なら「常に左から右」と決まってるんだけどね. ただし, 「関数を呼び出す前に実引数に伴う副作用は全て適用されている」ことは保証されてますし, scanf(%d %d", &yr, &profit[yr]); も動作は確定しています (規格に準拠した脳内コンパイラがあれば大丈夫). ちなみに「副作用のない」ように書くのはほぼ不可能....

bururutti-2
質問者

お礼

「処理系定義」ではなく「未定義」ですか。 自分には違いがよく分からないけど・・・ 回答有難うございました。

  • notnot
  • ベストアンサー率47% (4848/10261)
回答No.4

他の方が書いているように、言語仕様では未定義動作です。何が起こるかわからない。 具体的に何故そのようになるのかは、コンパイラの処理の仕方による。 (3)だと、gccコンパイラで最適化しないと、こんな感じ。 a ← 10 レジスタ ← a レジスタ += a b ← レジスタ a += 1 a += 1 二項演算子の左項と右項をどちらを先に評価するかとか、複数の実引数を使った関数呼び出しでどの順序に実引数を評価するかとか、等も未定義のはずです。 なぜ仕様上で未定義になっているかというと、コンパイラの最適化をうまく働かせるためです。

bururutti-2
質問者

お礼

なるほど、最適化し易くする為に未定義にしているのですか。 この処理は未定義だから予測不可能なのですね。 回答有難うございました。

  • shiren2
  • ベストアンサー率47% (139/295)
回答No.3

少し追記しておきますと、C言語のこういった動作は基本的に実装系依存です。 同じコンパイラの同じバージョンで行う限り、結果は一定するでしょうが、別のコンパイラや別のバージョンで試した時に結果が変わることがあり得ます。 そういった点に注意して、副作用のない書き方を心掛ける必要があります。

bururutti-2
質問者

お礼

これって実装系依存なのですか。 「式の前や後でインクリメントされる」のは自分が用いたコンパイラで実行した場合なんですね。 再度回答有難うございました。

  • shiren2
  • ベストアンサー率47% (139/295)
回答No.2

いい実験ですね。 前置は評価が行われる前にインクリメントされますよね。 >int b = ++a + ++a ; a+=1; a+=1; int b = a+a; >int b = a++ + a++ ; 後置は評価が行われた後にインクリメントされます。 int b = a+a; a+=1; a+=1; しかし、これは実験なので良いですが、実際のコーディングではこういった「副作用を伴いうる書き方」はしない方が無難です。 例えば下記のようなものですね。 出典は「プログラミング作法」です。 > str[i++] = str[i++] = ' '; > array[i++] = i; > scanf(%d %d", &yr, &profit[yr]);

参考URL:
http://www.amazon.co.jp/dp/4756136494/
bururutti-2
質問者

お礼

なるほど、式の前や後でインクリメントされていたのですね。 今回は式の中にインクリメントを織り交ぜたらどうなるか気になったので実験用としてプログラミングしてみましたが、確かに実際のコーディングではこのような書き方はしない方が良さそうですね。 回答有難うございました。

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

「やってはいけない」ことをしちゃったから. 詳細は面倒なので全部省きますが, ぶっちゃけて言うと 「同じオブジェクト (変数など) を 1つの式で 2回以上変化させたらダメ」 ということになっています. つまり, (3)~(6) はすべてこの「ダメなこと」をしちゃっているので, 結果として 「どうなるか誰も知らない」 ことになっています. 念のため言っておくと, (4) と (5) で「予想通りの結果になった」というのも「偶然」だと思ってください. この結果が言語として保証されているわけではありません.

bururutti-2
質問者

お礼

これって駄目な事だったんですか。結果は誰も予測出来ないのですね。 回答有難うございました。

関連するQ&A

  • C言語の演算について

    次のプログラムを実行したらどう出力されますか。 微妙な代入演算の違いが分からないので、教えていただけないでしょうか。 #include<stdio.h> void main (void) { int x = 5; int y = 8; int z = 3; int a,b,c,d,e,f; a = y == x + z; b = !x; c = x + y / z; d = x *=z - 1; e = --y / --z; f = y+++ % x++; printf("%d,%d,%d,%d,%d,%d\n",a,b,c,d,e,f); } できれば途中のトレースも書いていただけると助かります。 よろしくお願いします。

  • c言語の簡単な演算なのですがわかりません。

    int a = 10, b = 5, c = 3; b = ++a - c--; print("%d\n", b); b の結果が がなんで 8 になるのでしょうか? aのインクリで11 cのデクリで2 で9のような気がしましたが違いますね。 計算の順番をわすれてしまいました。 小学生レベルといわずよろしくお願いします。

  • C言語の演算式

    C言語のプログラミングで、4×(1-1/3+1/5-1/7+・・・・・)の式を100000項まで合計した式を作りたいです。答えは3.141591になります。 自分で作成してみたのですが、なかなか上手くいきません。 どうすればよいでしょうか?以下自分の作成したプログラムを「」内に載せます。 答えは0になってしまいます。 「 #include <stdio.h> int main(void) { int i=0,a=1,b; do{ i++; if(i%2) { a+=(-1)/(2*(i-1)+1); } else{ a+=1/(2*(i-1)+1); } }while(i<100000); b=4*a; printf("π=%f\n",b-1); putchar('\n'); return (0); } 」

  • プログラミングC言語の問題ですがわかりません。

    プログラミングC言語の問題ですがわかりません。 5つのint型変数a,b,c,ans,responseを宣言する。a,b,cの値はscanf関数で読みこむことにより初期化する。 a(1)b(2)c= と表示して、scanf関数によりユーザーの答案をresponseに読み込み、演算結果が正しければ1、間違っていたら0を表示するプログラムを作成しなさい。ここで(1)と(2)には算術演算子(+,-,*,/,%)がはいる。5×5=25通りの演算をすべて実行するようにしなさい。 特に正しければ1、間違っていたら0を表示するプログラムがわかりません。 まだprintfとscanf関数と演算子しかやっていないのでそれを踏まえたプログラムを1通りだけでいいので書いて頂けると幸いです。

  • C言語の演算について

    次のプログラムを実行したらどう出力されますか。 微妙な代入演算の違いが分からないので、教えていただけないでしょうか。 #include<stdio.h> void main (void) { int x = 5; int y = 8; int z = 3; int a,b,c,d,e,f; a = y == x + z; b = !x; c = x + y / z; d = x *=z - 1; e = --y / --z; f = y++ % x++; printf("%d,%d,%d,%d,%d,%d\n",a,b,c,d,e,f); } できれば途中のトレースも書いていただけると助かります。 よろしくお願いします。 なお、先ほど記述ミスがあるのにも関わらず投稿してしまいました…。 正しい記述はこちらの質問です。 大変失礼しました。 前の質問は削除可能になり次第、削除いたします。

  • C言語に詳しい方。教えてください。ずっと悩んでるのですがわかりません。

    問 次のプログラムに3重ループ(for)を使ってプログラムを作成したい。 コンパイルの実行結果から考えて、(1)(2)(3)の中に適切な数値や式等をいれて コンパイルの実行結果になるように3重ループを使って作成せよ #include<stdio.h> main() { int a,b,c; for(a=1;a<=3;a++){ (1) for(b=1;b<=2;b++){ (2) for(c=1;c<=3;c++){ (3) } コンパイルした後の実行画面 abbc c c d abbc c c d abbc c c d 解答欄 (1) (2) (3)

  • c言語について

    C言語で、二つの整数値を読み込んで、前者の値が後者の何%であるかを実数で表示するプログラムを作成しようとして以下のコードを書きました。 #include <stdio.h> int main(void) { int n1, n2 ; puts("二つの整数を入力してください。") ; printf("整数A : \n") ; scanf("%d" , &n1) ; printf("整数B : \n") ; scanf("%d" , &n2) ; printf("Aの値はBの%f%%です。\n" , (double)(n1 / n2) * 100) ; return 0 ; } 上記コードを実行すると、0.000000%のような結果になります。 そこで、最後のキャスト演算子を使用した後の式で(n1 / n2) * 100がまずいのかなと考え、n1 / n2 * 100にしたら上手くいきました。 なぜ、上記コードだと上手くいかないんでしょうか?

  • C言語の課題について。教えてください

    次のプログラムに3重ループ(for)を使ってプログラムを作成したい。 コンパイルの実行結果から考えて、(1)(2)(3)の中に適切な数値や式等をいれよ。 #include<stdio.h> main() { int a,b,c; for(a=1;a<=3;a++){ (1) for(b=1;b<=2;b++){ (2) for(c=1;c<=3;k++){ (3) } コンパイルした後の実行画面 abbc c c d abbc c c d abbc c c d 解答欄 (1) (2) (3)

  • 問題がとけません

    以下の設問に答えよ。 型はC言語の基本型でchar型を除いたもので答えよ。 1.次の演算結果の型と値を求めよ。 100/10*5 2.次の演算結果の型と値を求めよ。 1.0f/2 3.次の演算結果の型と値を答えよ。 100/10*5.0 4.次の演算結果の値を答えよ。 54321-54321/100*100 5.次の演算結果の値を答えよ。※(int)(式)は,式の値の小数点以下を切り捨ててint型に変換する。 (int)(123/5.0+0.5) 6.次の演算結果の型と値を答えよ。 5<2 7.次の演算結果の型と値を答えよ。 5.0>2 8.変数a,b,cに関して,a<b<cの真偽を評価する論理式を書け。 9.次の文が実行された後,変数i,kの値はいくらになっているか。変数はどちらもint型とする。 i=0; k=0; k=5+(++i); 10.次の文が実行された後,変数kの値はいくらになっているか。変数はどちらもint型とする。 i=5; k=5; k *= i<0 ? (-i) : i; 11.次の文Aは実行されるか。kの型はintである。文法的な誤りはない。 if(k = 0)  文A

  • C++のnew演算子について質問です。

    C++のnew演算子について質問です。 以下は関数にポインタを渡して値を得ようしたプログラムです。 ※ディレクティブは省略しています。 void test( int* a ) { a = new int( 100 ); } void main() { int* b; test(b); printf( "%d", *b ); delete b; b = NULL; } このプログラムを実行すると、コンソール画面には100と表示されるかと思っていたのですが、 実際には滅茶苦茶な値と、例外が発生して強制終了しました。 また、関数に渡したポインタのアドレスもNULLとなってしまいます。 そこで以下のようにソースを変更すると正常に100が表示されました。 void test( int** a ) { *a = new int( 100 ); } void main() { int* b; test(&b); printf( "%d", *b ); delete b; b = NULL; } 結果的には目的が達成できたのでいいのですが、なぜこのような動作をするのかが いまいち釈然としません。 new演算子は自動的には破棄されないのではないのでしょうか? 回答の程、よろしくお願いします。

専門家に質問してみよう