HN🔥 98
💬 155

【C言語クイズ】Int a = 5; a = a++ + ++a; この計算結果はいくつになる?

e-topy
3日前

ディスカッション (11件)

0
e-topyOP👍 98
3日前

C言語の未定義動作に関する有名な問題です。以下のコードを実行した際、変数aの値がどうなるか考えてみてください。a = a++ + ++a; なぜこのような結果になるのか、そして未定義動作(Undefined Behavior)とは何かを解説します。

1
magicalhippo
2日前

単に自分の知識不足か、C言語を忘れすぎているだけかもしれないけど(元々そこまで詳しくもなかったし)、なぜこれが未定義動作(UB)になるのかちょっと困惑してる。

こういう演算子を追加するときには「これは仕様を明確にしておくべきだ」という反応が起こるべきものだと思うし、実装も比較的簡単で理にかなった定義の仕方は少なくとも一つはあるはずなんだけど。

2
susam
約24時間前

投稿内のコード、2010年に自分が書いた記事(https://susam.net/sequence-points.html )のコードとそっくりだ。

  int a = 5;
  a += a++ + a++;

このコード(a=5の場合ですら)が、面接の質問としてよく使われていたのを覚えてるよ。こういう質問には本当にうんざりしてた。というのも、出題する面接官のほとんどが「自分のコンパイラの環境で出た出力こそが正解だ」と思い込んでいたから。もし「このコードは未定義動作です」って説明しようものなら、反応は「軽く否定される」か「深刻な混乱に陥る」かのどちらかだった。彼らのほとんどは、『未定義動作』や『シーケンスポイント』について理解もしてなければ、気にも留めてなかったね。

ある面接官のことは特に覚えてる。未定義動作の理由を説明したら、辛抱強く聞いてくれた後にこう言ったんだ。「正解は17ですよ。2つの後置インクリメントで変数は6になり、元の5に6を2回足すと17になるからね」って。

最近はこういう面接の質問が減って本当によかったよ。減ってるよね? 減ってると言ってくれ。

3
HelloNurse
約11時間前

aの最終値なんてどうでもいい、こんなコード書いたら即クビだ。人種差別的なジョークよりたちが悪い。

4
Aardwolf
約8時間前

なぜC言語でこの順序が定義されなかったのか?

符号付き整数のオーバーフローが恐ろしい未定義動作になるのは、多種多様なCPUアーキテクチャで扱いが異なっていたという事実で少なくとも説明はつく(とはいえ、uint16_t同士の左シフトで符号付きintが返ってきて、符号付き整数のあやふやな挙動に巻き込まれるという仕様は、個人的には許容しがたいけどね)。

でもこれは言語レベルで完全に定義可能なはず。CPU依存の要素なんて何もない。言語仕様で「文の実行順序は左から右へ」や「後置インクリメントは文の実行が完全に終わってから行う」といったルールを規定できたはずだ。私の言いたいのは、今適当に挙げたルールが完璧かどうかではなく、言語設計者が完全に定義できたはずだという点だよ。

5
Someone
約8時間前

ここで興味深いのは未定義動作(UB)であり、まあ……実際には2つのUBがあって、それのおかげで11、12、13という3つの正解があり得ることだ。

UBがあるんだから、どんな答えもあり得るんじゃないの?

6
Boxxed
約8時間前

ここで興味深いのは未定義動作(UB)であり、まあ……実際には2つのUBがあって、それのおかげで11、12、13という3つの正解があり得ることだ。

いや、未定義動作が発生した時点で、どんな結果でも起こり得るんだよ。

7
compiler-guy
約8時間前

未定義動作があると、準拠しているはずのコンパイラはやりたい放題になる。セグメンテーションフォールトを発生させようが何をしようが自由ってわけだ。

でも実際によくあるのは、「90年代半ばに書かれた個人のCコンパイラ」が(たぶん偶然)特定の実装をしてしまい、それを(おそらく非公式な)拡張機能として維持し続けているケースだ。そして間違いなく、それに依存しているユーザーがいて、様々な理由で移行できない。 anyway、ユーザーが新しいコンパイラをポンと入れてすぐに使えるわけじゃないなら、アップグレードを売り込むのは難しい。

言語レベルで見れば、それは未定義動作であり、それに依存するコードは言語仕様としてはバグであり、移植性もない。

もしこれを定義してしまったら、未定義だったものを無理やり定義しただけに過ぎないコンパイラは非準拠になってしまう。

おそらく最善の道は、何らかの定義を与えることではなく、これをエラーにすることだろう。そうすれば、動作がいつの間にか変わってしまうようなことはなくなる。

未定義動作のままでも言語レベルでは許容されるが、優れた実装ならユーザーのコードを警告なしに壊すようなことは避けるはずだ。

UBSanのような現代のコンパイラツールのおかげで、未定義動作の結果が変わるという問題は以前よりずっと小さくなった。ただ、ほとんどのUBは「診断不要」だから、今のツールを使わないユーザーは自分のコードに問題があることすら気づかないんだけどね。

8
sangeeth96
約7時間前

インドの学校や大学で、これが筆記試験のまともな質問だと信じている教師たちと戦わなきゃいけなかった。標準的な教科書がこぞってこの問題を載せていて「予測可能な出力が得られる」と主張していたから、彼らを100%責めることもできないんだけどね。自分も、いろんなコンパイラで実行して変な挙動に気づくまでは、あとまともな本でUBやシーケンスポイントについて読んでからは、そう思ってたよ。

運良く、いろんなコンパイラの出力を突きつけて、最後にはみんな鼻で笑ってやれたけどね。

9
tombert
約7時間前

この手のクソみたいな仕様が大嫌い。結果が100%確信できないという事実は、++演算子(前置だろうが後置だろうが)を避けるべきだと言っているようなものじゃないか?

最近はCをあまり書かないけど、使っていた頃でもインクリメントは必ず別々の行に書いてたし、a += 1か、いっそ a = a + 1 と書いてた。パフォーマンスが落ちたなんて感じたことは一度もないし、コードが読みにくくなったとも思わない。むしろ意味が曖昧じゃない分、こっちの方が分かりやすいと思うよ。

10
Timwi
約6時間前

その文はC#では有効なコードだよ。C#は左から右へ実行順序が定まっていて、未定義動作もない。答えは 5 + 7 = 12 になる。