int a = 5; a = a++ + ++a; の後、a はいくつになるのか?(2011 年の問題)

2026/05/12 23:14

int a = 5; a = a++ + ++a; の後、a はいくつになるのか?(2011 年の問題)

RSS: https://news.ycombinator.com/rss

要約

Japanese Translation:

C/C++ コードで結合された事前増分演算子と事後増分演算子を利用するもの(例:

a = a++ + ++a
)は未定義動作 (UB) を招き、コンパイラの実装および最適化に依存して 11 から 14 までの予測不可能な結果を出力します。Java や C# のように言語仕様で左から右の評価を強制するもの(例:JLS セクション 15.7)とは異なり、レガシーおよび現代的な C/C++ コンパイラはオペランドを取得する段階が異なります。例えば、gcc バリエーションは常に [6–14] の範囲の結果を返し、Clang バージョンは [5–13] のような値を生み出し、Microsoft の出力は演算子オーバーロードを有効にすると完全にシフトします。Borland Turbo C++、Keil C、SDCC、HiSoft C といった特定の歴史的コンパイラでも、同じ式に対して異なる、非移植性の結果セットを返します。これらの環境間で単一の標準的な答えが存在しないため、複数システムをターゲットとする開発者は論理エラー(コンパイラ依存の評価順序による)を防ぐためにこれらの構成を完全に回避する必要があります。

本文

C/C++ における未定義振る舞い(UB)に関する考察報告書

Furio 氏より提供された「パズル」と題する課題は、数日間にわたり、関心が薄い方も含め広く周知されました。本件の最も興味深い点は、未定義振る舞い(Undefined Behavior: UB) が複合的に存在することです。これにより、結果として 11、12、13 の 3 つの異なる答えが理論上可能となります。

以下にまず理論的な考察を述べ、次に実証的な結果について紹介いたします(初期データは nism0 氏による収集ですが、後にポーランド側のミラーサイトを通じて多数の参加者により拡張されました)。


1. 未定義振る舞い(UB)が生じる箇所

第一の UB:取得順序の不確定性

まず不明なのは、どの

a
の値が先にメモリからレジスタへコピーされるかという点です。2 つの解釈可能性が存在します。

可能性 1:最初の

a
を先に取得するパターン

// a = a + ++a;
// ステップ 1: 最初の a を読み出す (a == 5)
// ステップ 2: 前置インクリメントを実行 (a == 6)
// ステップ 3: 二番目の a を読み出す (値は 6)
// ステップ 4: 加算計算
// 結果:11

可能性 2:前置インクリメントを先に処理し、その結果をメモリに格納してから両方の

a
を取得するパターン

// a = a + ++a;
// ステップ 1: 前置インクリメントを実行 (a == 6) -> メモリへの書き込み
// ステップ 2 & 3: 両方の a を読み出す (値はどちらも 6)
// ステップ 4: 加算計算
// 結果:12

このように、単に後置インクリメントを無視した場合においても、11 と 12 という 2 つの異なる結果が生じる可能性があります。

第二の UB:後置インクリメントと代入の競合 (
a = a++
)

a = a++
という一行には、以下の 2 つのパターンが存在します(
int a = 5;
を例として考察)。

用語の定義:

  • a_mem
    : メモリ上に保持された
    a
    の値
  • a_copy
    : レジスタなどにコピーされた
    a
    の値

可能性 1:後置インクリメントの結果が「行方不明」になるパターン

初期状態: (a_mem == 5, a_copy == なし)
ステップ 1: a を読み出す -> (a_mem=5, a_copy=5)
ステップ 2: メモリ上の後置インクリメントを実行 -> (a_mem=6, a_copy=5) ※ここでコピーされた値は古いまま維持される
ステップ 3: 代入を実行 -> a_copy(5) が a_mem に書き戻される -> (a_mem=5, a_copy=無効化)
結果:後置インクリメントの結果が上書きされ、実質的に無効化される。

可能性 2:後置インクリメントを計算の最末端まで遅延させるパターン

初期状態: (a_mem == 5, a_copy == なし)
ステップ 1: a を読み出す -> (a_mem=5, a_copy=5)
ステップ 2: 代入を実行 -> (a_mem=5, a_copy=5) ※値は変更されない(コピーが元の値を参照)
ステップ 3: メモリ上の後置インクリメントを実行 -> (a_mem=6, a_copy=無効化)
結果:計算終了直後に後置インクリメントが適用される。

UB のまとめ (
a = a++ + ++a
の場合)

上記の 2 つの UB を組み合わせると、以下の 4 つの結果が理論的に導き出されます。

前処理パターン後置インクリメント振る舞い加算に使用される値結果
可能性 1 (先読み)可能性 1 (結果失効)5 + 611
可能性 1 (先読み)可能性 2 (最終実行)5 + 612
可能性 2 (増分後読み)可能性 1 (結果失効)6 + 612
可能性 2 (増分後読み)可能性 2 (最終実行)6 + 613

2. 実証的結果

以下の表は、nism0 氏による初期テストと、nonek、qyon 氏らによる修正版(タイポ訂正を含む)を示しています。

コードパターンCode 1
a=a+++a++
Code 2
a=a+++a
Code 3
a=++a+a++
Code 4
a=++a+++a
Code 5
a=a++
Code 6
a=a+++a
gcc 2.9512131413612
gcc 4.112131413612
gcc 4.212131413612
gcc 4.2.1 (Apple)12131413612
gcc 4.312131413612
gcc 4.3.312131314612
gcc 4.4.412131314612
gcc 4.6.0 (exp.)12131413612
gcc 4.5.1 MinGW6412131314612
tcc 0.9.25????????512
bcc 0.16.17????????512
Microsoft C/C++ (80x86)12131314612
Embarcadero C++ (Win32)12131314612
Intel C++12121313612
Keil C2111212136
SDCC#6092111213145
clang 2.811121213511
clang 1.6 (Apple)11121213511
PHP 5.2.1011121213512
java 1.6.0_0611121213511
javac 1.4.2_1211121213511
java 1.6.0_2111121213511
javac 1.6.0_2211121213511
C# 2.011121213511
C# 4.011121213511
C# Mono 2.6.411121213511
Borland Turbo C++ (DOS)12131314612
HiSoft C (ZX Spectrum)11121213512

※補足:Garbaty lamer 氏は、ポーランド側のミラーサイトにて HiSoft C for ZX Spectrum のスクリーンショットを投稿されています(クリックすると拡大可能)。

追加のテストコード提供

Icewall、Krzysztof Kotowicz(PHP)、mlen(Clang/GCC)、none'a(Java/Keraj)、MDobak(SDCC/Keil)、garbaty lamer(C# その他)、Xgrzyb90(GCC)、no_name(GCC)、dikamilo(MinGW)等の皆様に、追加の結果をご提供いただきました。

さらに、ポーランド側のミラーサイトにて以下のコード(nism0 氏作成)を試すことができます(第 3 節参照)。


負荷オーバーロードテスト (付録 1)

ポーランド側で Rolek 氏は、オーバーロード演算子にも同様のテストを適用することを提案しました。MSVC++ および g++ による結果は以下の通りです。

コードパターンCode 1
a=a+++a++
Code 2
a=a+++a
Code 3
a=++a+a++
Code 4
a=++a+++a
Code 5
a=a++
Code 6
a=a+++a
MSVC++ (オーバーロードなし)12131314612
MSVC++ (オーバーロードあり)11131214512
g++ 4.5.0 MinGW (オーバーロードなし)12131314612
g++ 4.5.0 MinGW (オーバーロードあり)11131214512

その他の重要な情報

シーケンスポイントに関する文献

krlm 氏は、シーケンスポイントに関する優れた記事をリンクとして提供されています。

C# での挙動 (付録 2)

Garbaty lamer 氏は、C# ではこのパズルは実際には「難問」ではなく、明確に定義されていると述べています(C# 仕様 §7.3)。

「式内の演算子は左から右の順序で評価されます。例:

F(i) + G(i++) * H(i)
の場合、F は i の旧値を、G は i の旧値を、H は i の新値をそれぞれ用います。これはオペランドの優先順位とは無関係です。」

Java での挙動

Cem Paya 氏のコメントによると、Java でも厳密に左から右の評価が行われるため、同様に難問ではありません(Java Language Specification section 15.7)。

コンパイル最適化の影響 (C++ 特有)

C++ では未定義であるとされていますが、コンパイル時の最適化レベルによって挙動が変わることがあります。例えば、コンパイラは「

a
の値は評価中に不変である」と想定し、他の参照をその取得した値と同じものとして最適化を行う場合があります。


付録:テスト用ソースコード (付録 3)

#include <stdio.h>

int main(void){
    int a = 5, b = 5, c = 5, d = 5, e = 5, f = 5;
    
    // test 1
    a = a++ + a++;
    printf("%i \n",a); 
    
    // test 2
    b = b++ + ++b;
    printf("%i \n",b); 
    
    // test 3
    c = ++c + c++;
    printf("%i \n",c); 
    
    // test 4
    d = ++d + ++d;
    printf("%i \n",d); 
    
    // test 5
    e = e++;
    printf("%i \n",e); 
    
    // test 6
    f = f + ++f;
    printf("%i \n",f); 
    
    // done
    return 0;
}

付録 2: マスタークラス・バイナリファイルワークショップへの案内

バイナリファイルおよびプロトコルスキルを向上させたい方へ、4 月〜6 月に開催するワークショップ「Mastering Binary Files and Protocols: The Complete Journey」をお勧めいたします。


※注記:本文書内の表において一部の行が欠落している箇所や、原文のフォーマット上の不整合が見受けられる場合(例:

??
の連続など)は、データ取得時の未確認結果またはコンパイラ固有のエラー/挙動を反映したものです。

同じ日のほかのニュース

一覧に戻る →

2026/05/15 2:08

2024年型 RA V4 ハイブリッド車からのモデムおよび GPS の取り外しについて

## Japanese Translation: **改善された概要:** 現代の車両は常時オン状態のコンピューターとして機能し、生体認証データや位置情報履歴、性的活動に至るまでを含む膨大な量の機密ユーザーデータをデフォルトで収集し、LexisNexis や Verisk などの第三者ブローカーに送信しています。過去のセキュリティインシデントはこの接続に伴うリスクを浮き彫りにしており、2015 年のジープチェロキーハッキング事件(機械システムの完全な制御が可能となる)から、最近のテスラカメラへの侵害やマツダ遠隔アクセス脆弱性まで含まれます。プライバシーを取り戻すためには、車両のデータ通信モジュール(DCM)および GPS システムを物理的に無効化することが可能です。この手順は 2024 年式 RAV4 ハイブリッド車での実証がなされています。しかし、この介入により、空気中更新(Over-the-Air アップデート)、自動事故通報アラート、SOS サービス、トヨタクラウドサービスなどの重要な機能が無効化されます。位置情報の漏洩を防止するためには、DCM と GPS の配線を完全に切断することが不可欠です。CarPlay などの接続型インフォテインメントシステムを通じて位置情報が漏洩するのを防ぐためです。さらに、Bluetooth タンデリング(有線接続によるデータ共有)はデータ漏洩のリスクがあるため避け、ローカルアクセスには有線 USB 接続またはアダプターの使用を推奨します。この修復策は、特定のバイパスキットを使用して計測情報の送信を止めるドライバーに権限を与えますが、マグノーソン・モス保証法に基づきクラウド保証の適用除外になる可能性があり、特定の工具和部品を用いた精密な機械作業を必要とします。

2026/05/15 7:44

「Millions」によるポンド節約:難民処理システムにおいてパランティルテクノロジーへの置き換えが実現

## Japanese 翻訳: 内閣府住宅・コミュニティおよび地方政府省(MHCLG)は、元の Palantir Foundry プラットフォームを、内部専門家が構築した自主開発システムに置き換えました。この決定は、Palantir の初期の無料提供が、公正な競争を必要とする公的調達原則に違反するという懸念に基づいており、国民監査局(NAO)の報告書でも強調された点です。また、これは政府全体としての Palantir から距離を置く意欲も反映しています。MHCLG は、新しいソリューションが柔軟性が高く、データとコードを完全に管理でき、より高いセキュリティ基準を満たし、外部サプライヤーへの依存を削減するとともに、年間数百万ポンドの運用コストを節約できることを明言しました。元の Palantir システムは、6 ヶ月の間、英国政府の「ウクライナからの家」計画において無料にて支援し、その間に 15 万 7,000 人以上の難民が再定住されましたが、新しい内部システムに比べれば柔軟性に欠けると判断されました。シニアデジタルリーダーのココ・チャン氏は、外部への依存を減らしながら年間費用を削減するという点で、内部ソリューションへの移行を示しました。元政府技術顧問のターネンス・エデン氏はこの移行を、重要な国策としての「主権テクノロジ」への一歩として捉え、重要インフラであるデジタル基盤に対する国家的コントロールを保証すると述べています。外部の視点では、自主開発には利点があるものの、有経験の専門家によって迅速な展開スキルを提供されることは緊急プログラムにとって不可欠であり(BCS 副会長のエマ・ロガン氏)、大規模テック企業への依存を減らすためには投資スケジュールの検討が必要であると指摘されています(Public Digital コンサルタントのロブ・ミラー氏)。Palantir は、そのシステムが軍事支援、地雷除去、ウクライナにおける戦犯捜査を含むより広範な取り組みの一部であったことを確認し、社名変更は企業が排他的な利用に锁定されるリスクがないことを示していると表明しました。新しいシステムは 2025 年 9 月までに稼働予定で、長期的なプログラムのニーズに応えることを目指しています。この移行は、MHCLG が主権テクノロジを追求する過程での最も重要な一歩となりつつあり、内部統制と展開専門性のバランスを保証しています。

2026/05/15 5:22

ついに Amazonbot も robots.txt の指示を尊重し始めたようです。

## Japanese Translation: このウェブサイトは、Techaro から提供される防ボットサービス Anubis(現在展開されているバージョンは v1.25.0)を活用することで、ユーザーの安全を最優先しています。当サイトは自動化された脅威への対策として機能しており、独自のマスコットキャラクター(CELPHASE によるデザイン)を採用し、誇りを持ってカナダで開発されています。これらの要素は、高度なボット保護とカスタムブランドリングを組み合わせ、カナダ発の開発に根ざした安全な環境を実現しています。