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回出てきたら、こける」というのも考えられます(バグではない点に注意)
こういうことで、「インクリメントしてから云々」という解説も、あるいは、誤解の元かなと思います。
同じようなことは、括弧や演算子の(結合の)優先順序にも言えて、入門書で、
・括弧の中は「先に」計算する
・乗除は加減より「先に」計算する
という記述があることもあります。
これも、実は、「括弧の中は『強く』結合する」だけで、括弧の外だけを先に計算しておいて、あとから、括弧の中を計算するということも、実はあるかもしれません。
なるほど、++aやa++が2回以上出てこない事を前提にして定義しているのですね。
インクリメントするタイミングまでは定められていないのですね。
回答有難うございました。
No.5
- 回答日時:
うぃ, 特定の演算子 (論理演算子の &&, || とコンマ演算子) 以外ではオペランドの評価順序は定義されていませんし, 関数の実引数の評価順序も決まってません>#3. 実際には「処理系定義」じゃなくて「未定義」なので「全く同じ式であっても位置によって順序が違う」ということも許されています.
Java なら「常に左から右」と決まってるんだけどね.
ただし, 「関数を呼び出す前に実引数に伴う副作用は全て適用されている」ことは保証されてますし,
scanf(%d %d", &yr, &profit[yr]);
も動作は確定しています (規格に準拠した脳内コンパイラがあれば大丈夫).
ちなみに「副作用のない」ように書くのはほぼ不可能....
No.4
- 回答日時:
他の方が書いているように、言語仕様では未定義動作です。
何が起こるかわからない。具体的に何故そのようになるのかは、コンパイラの処理の仕方による。
(3)だと、gccコンパイラで最適化しないと、こんな感じ。
a ← 10
レジスタ ← a
レジスタ += a
b ← レジスタ
a += 1
a += 1
二項演算子の左項と右項をどちらを先に評価するかとか、複数の実引数を使った関数呼び出しでどの順序に実引数を評価するかとか、等も未定義のはずです。
なぜ仕様上で未定義になっているかというと、コンパイラの最適化をうまく働かせるためです。
なるほど、最適化し易くする為に未定義にしているのですか。
この処理は未定義だから予測不可能なのですね。
回答有難うございました。
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/
なるほど、式の前や後でインクリメントされていたのですね。
今回は式の中にインクリメントを織り交ぜたらどうなるか気になったので実験用としてプログラミングしてみましたが、確かに実際のコーディングではこのような書き方はしない方が良さそうですね。
回答有難うございました。
No.1
- 回答日時:
「やってはいけない」ことをしちゃったから.
詳細は面倒なので全部省きますが, ぶっちゃけて言うと
「同じオブジェクト (変数など) を 1つの式で 2回以上変化させたらダメ」
ということになっています. つまり, (3)~(6) はすべてこの「ダメなこと」をしちゃっているので, 結果として
「どうなるか誰も知らない」
ことになっています.
念のため言っておくと, (4) と (5) で「予想通りの結果になった」というのも「偶然」だと思ってください. この結果が言語として保証されているわけではありません.
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- C言語・C++・C# (C言語)めちゃくちゃな値になってしまいます。 5 2022/08/13 11:55
- Java javaでのプログラム(配列)について質問です. 2 2022/10/14 22:27
- C言語・C++・C# c言語の問題です 課題1 (二分探索木とセット) 大きさ size の配列 array を考える。す 2 2023/01/10 21:08
- C言語・C++・C# プログラミング c言語 4 2023/03/07 01:05
- C言語・C++・C# C 言語の Gauss Jordan 法について 2 2022/12/28 11:16
- C言語・C++・C# 3×3のラテン方陣をつくるプログラムを作成したのですが、(↓) #include <stdio.h> 5 2023/07/10 01:53
- C言語・C++・C# C言語のエラーについて 2 2022/07/11 13:56
- C言語・C++・C# 10人分の生徒の英語の点数{32,34,41,38,40,26,14,46,42,50} と数学の点 2 2022/05/26 21:31
- C言語・C++・C# プログラミングのペーパーテスト 実行結果の表示を答えてください #include <stdio.h> 2 2022/07/09 16:14
- C言語・C++・C# C言語 3 2022/10/04 15:07
関連するカテゴリからQ&Aを探す
おすすめ情報
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
DWORDの実際の型は何でしょうか
-
2重定義って??
-
typedef enumの使い方を教えて...
-
C++のfor文について
-
long型の定数の末尾にLを付ける...
-
構造体の要素すべてに対する四...
-
構造体の宣言でエラーが出ます。
-
enumについて
-
VC6でlong longでエラー?
-
エラー「invalid conversion fr...
-
クラス間の変数について
-
void func( void )について
-
C++でboolにintの値を代入する...
-
dequeにconstが混ざったアイテ...
-
構造体を生成時にわざわざ初期...
-
C言語のコンパイルエラー
-
構造体
-
visualstudio C# テキストボッ...
-
スレッドのスケジューリングポ...
-
C言語でmain関数でのreturnとexit
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
DWORDの実際の型は何でしょうか
-
long型の定数の末尾にLを付ける...
-
2重定義って??
-
visualstudio C# テキストボッ...
-
C++のfor文について
-
構造体の要素すべてに対する四...
-
typedef enumの使い方を教えて...
-
ハンドルされていない例外が発...
-
変数の型を定義しなかった場合...
-
関数の実体定義にヘッダファイ...
-
intとINTの違いは?
-
【#define】 defineで定義した...
-
C++でboolにintの値を代入する...
-
構造体の宣言でエラーが出ます。
-
main.c:7:43: warning: implici...
-
プログラムの中で別のmainを呼...
-
void func( void )について
-
エラー「invalid conversion fr...
-
C言語 宣言した変数になにも代...
-
0除算を判定したい
おすすめ情報