インクリメントとは
インクリメント(increment)は、一般的には増加、増量、増分、増大といった意味を持ちますが、
コンピュータ用語としては、
変数の値を1だけ増加させる演算を指します。反対に、変数の値を1だけ減少させる演算はデクリメント(decrement)と呼ばれます。
インクリメントの概要
インクリメント操作は、プログラムにおけるループ処理のカウンタや、配列などのデータ構造を順に処理する際に頻繁に用いられます。そのため、多くのプロセッサではインクリメント操作を高速に行うための専用命令が用意されています。また、
プログラミング言語のレベルでも、インクリメントを行うための「インクリメント演算子」(例: `++`)が提供されていることが一般的です。さらに、
コンパイラによっては、インクリメント演算子をプロセッサの専用命令に最適化する場合があります。
しかしながら、「加算/減算」という概念とは別に「インクリメント/デクリメント」という概念を扱うことは、プログラミング初心者にとって煩雑になりがちです。また、経験豊富なプログラマであっても、演算子の評価順序を誤るとバグを引き起こす可能性があります。マイコンレベルでは必須の機能というわけではないため、インクリメント命令を持たないプロセッサも存在します。さらに、現代の
コンパイラの最適化が進んでいる場合、インクリメント演算子を使用しても算術演算子を使用しても、生成される機械語コードに違いがなく、処理速度が変わらない場合もあります。そのため、インクリメントという概念をあえて排除するプログラミングスタイルも存在します。
情報処理技術者試験で用いられる仮想計算機COMET IIの命令セットには、インクリメント命令は存在しません。
Pascalにはインクリメント演算子はありませんが、Object
Pascal(
Delphi)には`System.Inc()`手続きと`System.Dec()`手続きが存在し、Free
Pascalにも`Inc()`手続きと`Dec()`手続きが実装されています。これらの手続きは、場合によっては最適化されたコードに置き換えられます。
PythonやVisual Basic
.NETにはインクリメント演算子は存在しませんが、`++x`というコードは単項の`+`演算子を2回適用する`+(+x)`と同様に解釈されます。そのため、変数`x`が数値の場合、構文エラーにはならず、値をそのまま返す式として評価されます。
プロセッサでのインクリメント
通常、レジスタに数値を加算するには、加算命令とレジスタを示すための1
ワードと、加算する数値を表す1
ワード、合計2
ワードが必要となります。しかし、インクリメント命令を使用すれば、1
ワードで処理を完了できます。ただし、プロセッサによっては、インクリメント命令は加算命令よりもオペランドの種類が制限される場合があります(例:アキュムレータのみ)。
アセンブラの中には、1を加算する加算命令をインクリメント命令に最適化するものもあります。
Windows APIでは、スレッドセーフなアトミック操作のための関数として`InterlockedIncrement()`などが提供されています。
.NETには`System.Threading.Interlocked`クラスに`Increment()`メソッドなどが用意されています。
Javaには`java.util.concurrent.atomic.AtomicInteger`クラスに`incrementAndGet()`メソッドなどが存在します。これらの関数やメソッドは、
コンパイラによってアトミック命令などを使用した排他制御に置き換えられます。
高水準言語でのインクリメント
C言語、
C++、C#、
Java、
JavaScriptなどの
プログラミング言語では、インクリメント演算子`++`が用意されています。インクリメント演算子には、
前置インクリメント(例: `++x`)と
後置インクリメント(例: `x++`)の2種類があります。
オペランドが数値型の場合、どちらの演算子も値を1だけ増加させますが、式としての値が異なります。前置インクリメントは、インクリメント後の値を返します(`x += 1`と同じ)。一方、後置インクリメントは、インクリメント前の値を返します。
C++では、演算子オーバーロードにおいて、前置インクリメントと後置インクリメントを区別するために、後置インクリメントに余分な`int`型引数を記述します。しかし、古い
C++コンパイラでは、この記述に対応していない場合があります。
デクリメント演算子`--`も同様に、前置デクリメントと後置デクリメントが存在します。
これらの前置と後置というわずかな違いによって、プログラムの挙動が変わるため、可読性が低下したり、バグの原因となることがあります。Swiftでは、可読性の優先や言語学習コストの低減などの観点から、インクリメント演算子とデクリメント演算子が廃止されました。
真理値とインクリメント
アセンブリ言語や初期の
プログラミング言語では、真理値を表す専用のデータ型(
ブーリアン型)を持たず、
整数型で代用することが一般的でした。整数で代用する場合、0を偽(false)として扱い、0以外をすべて真(true)として扱います。
C言語は、この仕様を引き継いでおり、論理演算の結果は基本
整数型`int`となります。
ブーリアン型として
整数型を使用すると、本来インクリメントで記述する必要のない箇所で誤って使用されることがあります。例えば、真理値を表すフラグ変数を偽から真に変更する場合、通常は固定値を代入しますが、
整数型では0をインクリメントすると1になるため、代入の代わりにインクリメントでも変更できてしまいます。
ソフトウェア工学や
安全工学が未発達な時代には、不適切なインクリメント命令の使用が致命的なバグを生み、重大な事故を引き起こした事例があります。
放射線療法機器のセラック25は、1985年から1987年にかけて、少なくとも5人の患者を死亡させる重大な被曝事故を起こしました。事故原因には複数の要因がありますが、セラック25では従来の電気機械式の安全保護装置をソフトウェア制御に置き換えていました。装置を制御する
コンピュータの
オペレーティングシステムにおいて、安全性を確保するためのソフトウェア・インターロックに使用されていたフラグ変数(Class3)が、設定テストのルーチンを通過するたびにインクリメントされるようになっていました。Class3は1バイトで表現される変数であり、値が255のときにインクリメントするとオーバーフローが発生し、0に戻ってしまいます。これにより、本来はフラグが真でなければならない場面で偽となり、安全チェックがバイパスされてしまいました。このバグが原因で、患者に過剰な放射線が照射され、患者が死亡する事故が発生しました。
C++では、bool型が存在しますが、当初はbool型に対するインクリメントが許容されていました。bool型はtrueにインクリメントしてもtrueのままという仕様でしたが、
C++98の時点ですでにbool型へのインクリメントは非推奨となっており、
C++17ではインクリメント演算子をbool型に適用することが禁止されました。
JavaやC#のような後発の安全な言語では、
ブーリアン型へのインクリメントは当初から禁止されています。
ネーミングでの使用
C++や
Notepad++のように、ソフトウェアの改良版であることを示すためにインクリメント演算子`++`が使用されることがあります。
また、`increment`の形容詞形`incremental`は、
インクリメンタルサーチやインクリメンタルビルド(増分ビルド)といったソフトウェア機能の名称としても使用されます。
インクリメントは、プログラミングにおける基本的な演算の一つであり、理解しておくことは非常に重要です。効率的な処理が可能になる一方で、注意して使用する必要があることを覚えておきましょう。