初歩的な質問なのですが、以下のプログラムを実行した際に
printf()で出力している変数aとnの値が、それぞれ6と7となります。
どちらもn=5としたときに n++ + 1を求めているのですが
なぜ結果が異なるのでしょうか?
a=6, n=6 になると思ったのですが…
---------------------------------------
int n = 5; // n = 5で初期化
int a = 0;
a = n++ + 1;
printf("a = %d\n", a); // a = 6
n = 5; // 再度,n = 5で初期化
n = n++ + 1;
printf("n = %d\n", n); // n = 7
---------------------------------------
n = n++ + 1 と a = n++ + 1の違い
Re:n = n++ + 1 と a = n++ + 1の違い
n = n++ + 1;
は副作用完了点の間にnに対する複数の副作用が存在するため,「未定義」です。
つまり,「不正な」プログラム,ということです。
# ISO/IEC 9899:1999 6.5 Expressions / Paragraph. 2
n = 6であろうがn = 7であろうが,それどころかプログラムがクラッシュしようが鼻から悪魔が出てこようが,
それはコンパイラが間違った解釈をしたわけではなく,
「やってはいけないこと」をやったプログラマが悪い,ということです。
# 鼻から悪魔を出す実装があるなら,それはそれで動かしてみたいですが。
は副作用完了点の間にnに対する複数の副作用が存在するため,「未定義」です。
つまり,「不正な」プログラム,ということです。
# ISO/IEC 9899:1999 6.5 Expressions / Paragraph. 2
n = 6であろうがn = 7であろうが,それどころかプログラムがクラッシュしようが鼻から悪魔が出てこようが,
それはコンパイラが間違った解釈をしたわけではなく,
「やってはいけないこと」をやったプログラマが悪い,ということです。
# 鼻から悪魔を出す実装があるなら,それはそれで動かしてみたいですが。
Re:n = n++ + 1 と a = n++ + 1の違い
n = n++ + 1; は(規格上は)未定義の動作ですが、処理系が特定の振る舞いを規定しているのであれば、そのように動きます。
今回の質問では処理系不明なので、一般論としては何が起きるか分かりません。
# コンパイラによって鼻から悪魔が出るような健康被害を受けた場合は、当然クレーム物ですね。
今回の質問では処理系不明なので、一般論としては何が起きるか分かりません。
# コンパイラによって鼻から悪魔が出るような健康被害を受けた場合は、当然クレーム物ですね。
Re:n = n++ + 1 と a = n++ + 1の違い
未定義なんですね、でも未定義の場合は、Cの規格上、どのような実装でも許されるのですよね?ということはコンパイルエラーにしてもいいということですね。
Re:n = n++ + 1 と a = n++ + 1の違い
文法上はどこにも間違いがないので、コンパイルエラーにするのはまずいと思います。
実行ファイル作成までは正しく行なえて、実行結果がどうなるかはわからない、
というのがホントのところなのでありましょう。
ここで個人的な疑問。
n = 5;
n = n++ + 1;
printf("n = %d\n", n);
のコードにおいて、nに対して何をしているかというと
1)5に初期化
2)インクリメント
3)1を加える
4)値を出力
の4つですよね(2と3の順序は逆かもしれない)。
ということは、7と出力することしかあり得ないのではないかと
思ってしまったりするのですが、この考えはやっぱりおかしいですか?
実行ファイル作成までは正しく行なえて、実行結果がどうなるかはわからない、
というのがホントのところなのでありましょう。
ここで個人的な疑問。
n = 5;
n = n++ + 1;
printf("n = %d\n", n);
のコードにおいて、nに対して何をしているかというと
1)5に初期化
2)インクリメント
3)1を加える
4)値を出力
の4つですよね(2と3の順序は逆かもしれない)。
ということは、7と出力することしかあり得ないのではないかと
思ってしまったりするのですが、この考えはやっぱりおかしいですか?
Re:n = n++ + 1 と a = n++ + 1の違い
> 文法上はどこにも間違いがないので、コンパイルエラーにするのはまずいと思います。
コンパイルエラーでも構いません。
JIS X3010:2003の3.4.3 未定義の動作(undefined behavior)によると、
参考 未定義の動作に対して, その状況を無視して予測不可能な結果を返してもよい。翻訳時又はプログラム実行時に, 文書化された, 環境に特有な方法で処理してもよい(診断メッセージの発行を伴っても伴わなくてもよい。)。さらに(診断メッセージを出力し)翻訳又は実行を中断してもよい。
とあります。
コンパイルエラーでも構いません。
JIS X3010:2003の3.4.3 未定義の動作(undefined behavior)によると、
参考 未定義の動作に対して, その状況を無視して予測不可能な結果を返してもよい。翻訳時又はプログラム実行時に, 文書化された, 環境に特有な方法で処理してもよい(診断メッセージの発行を伴っても伴わなくてもよい。)。さらに(診断メッセージを出力し)翻訳又は実行を中断してもよい。
とあります。
Re:n = n++ + 1 と a = n++ + 1の違い
ふむ、それでは
[鼻から悪魔をだす可能性があるので、コンパイルを中断します。]
というエラーメッセージや警告を出すコンパイラがあってもいいわけですね。
そんなコンパイラ使ってみたい。
[鼻から悪魔をだす可能性があるので、コンパイルを中断します。]
というエラーメッセージや警告を出すコンパイラがあってもいいわけですね。
そんなコンパイラ使ってみたい。
Re:n = n++ + 1 と a = n++ + 1の違い
> ということは、7と出力することしかあり得ないのではないかと
> 思ってしまったりするのですが、この考えはやっぱりおかしいですか?
状況を無視して、例えばnをトラップ表現に設定する可能性があるとかは別として...
1)5に初期化
2)n++ + 1を評価
a) n++の結果として5を得る
b) 1を評価して1を得る
3)5 + 1を評価して6を得る
4)結果を格納する
a) n ← 6 (=の副作用)
b) n ← 6 (++の副作用)
というのもありえます。
> 思ってしまったりするのですが、この考えはやっぱりおかしいですか?
状況を無視して、例えばnをトラップ表現に設定する可能性があるとかは別として...
1)5に初期化
2)n++ + 1を評価
a) n++の結果として5を得る
b) 1を評価して1を得る
3)5 + 1を評価して6を得る
4)結果を格納する
a) n ← 6 (=の副作用)
b) n ← 6 (++の副作用)
というのもありえます。
Re:n = n++ + 1 と a = n++ + 1の違い
みなさん、たくさんの回答ありがとうございいます。
「未定義」だったのですね。勉強になりました。
確かにコード自体は意味のないものなので未定義でも問題ないですね。
ただ、「未定義ですよ~」とコンパイラが教えてくれてもいいような…
甘えすぎですかね。
「未定義」だったのですね。勉強になりました。
確かにコード自体は意味のないものなので未定義でも問題ないですね。
ただ、「未定義ですよ~」とコンパイラが教えてくれてもいいような…
甘えすぎですかね。