プログラミングにおいて定数(「ていすう」または「じょうすう」、: constant)とは、変数同様プログラムソースコードにおいて、扱われるデータを一定期間記憶し必要なときに利用できるようにするために、データに固有の名前を与えたものである。 ただし変数とは異なり、一度初期化するとその内容()を変更することはできない。よって、内容が変化しないことが保証される名前が必要なときに使用される。

ソースコードに直接記述するデータ(リテラル)のことを指して定数と呼ぶことがあり、標準規格での用語がそうなっている言語もある[要追加記述]。しかし、この記事で扱う「定数」はデータに名前を与えるものであり、基本的にリテラルとは別である。リテラルについてはそちらの記事を参照。なお、言語によっては、定数を初期化する式にリテラルあるいはリテラルと演算子等のみから成る式しか許さないことがある、というような関連はある。

定数の使用方法 編集

定数を扱うための操作は初期化と参照の二つがある。

初期化 編集

これから使用する定数の名前と対応するデータを明示する。変数はプログラミング言語によっては宣言が不要なものもあるが、定数は値が変えられないため、名前の明示(宣言)と同時にデータを関連づける(代入する)ことになる(初期化または定義)。また、定数も変数同様データ型を指定することもある。

言語によってサポート状況や構文は異なるが、定数には大別して「再代入できない変数」と「コンパイル時定数」[1]の2種類がある。前者の代入・初期化には、リテラルのほかに変数を含む任意の式も使用することができる。後者の代入・初期化には、リテラルあるいは同様のコンパイル時定数およびコンパイル時定数式しか使用できない。Pythonなど、動的プログラミング言語の中には定数をサポートしないものもあるが、読み取り専用プロパティで代用することができるものもある。

「再代入できない変数」としての定数の値は、プログラム実行時に初期化する際の右辺式の評価結果により決まる。つまり、サブルーチンなどで定数の初期化が何度か実行される場合は、呼び出しのたびに実際のデータが変化することもある。

以下のC言語における例では、定数yの初期化に変数を使用しており、仮引数xの値(実引数)によってyの値は実行時に変化する。このyは数学や物理学などにおける定数数学定数物理定数など)とは異なり、「再代入できない変数」を表している。

double func(double x) {
    const double y = x * x;
    /* 定数への再代入は不可能 */
    /*
    y = 0.0;
    */
    return y;
}

一方、「コンパイル時定数」としての定数の値は常に静的に定まる。C言語およびC++const型修飾子 (type modifier) は文脈によって意味が異なり、再代入できない変数を定義する目的のほか、コンパイル時定数を定義する目的にも使われる。

C#const修飾子 (modifier) は常にコンパイル時定数の定義に使用される。ただし将来的に変更される可能性のある定数をアセンブリ外部に公開する場合、アセンブリを再コンパイルすることなくバイナリ互換性を維持するには、constの代わりにreadonlyを使用することが推奨されている[2]

const double ConstantValue1 = 0.5;
const int ConstantValue2 = (int)(ConstantValue1 * 100.0) + 1; // 51
//const double SquareRoot2 = System.Math.Sqrt(2.0); // 不可。

C++11規格以降のC++では、コンパイル時定数の定義に使用可能なconstexpr指定子 (specifier) をサポートするようになった[3]

constexpr double ConstantValue1 = 0.5;
constexpr int ConstantValue2 = (int)(ConstantValue1 * 100.0) + 1; // 51
//constexpr double SquareRoot2 = std::sqrt(2.0); // 不可。
const double SquareRoot2 = std::sqrt(2.0); // 可。

参照 編集

初期化した定数を利用することを参照という。定数は宣言時に必ず初期化を伴うので、言語仕様の未定義動作を突くようなことがない限り、不正な値が使用されることはない。

初期化に関連する未定義動作の例として、C++11規格よりも前のC++では、静的ローカル変数の初期化はスレッドセーフ性が保証されないため、複数のスレッドからの同時初回アクセスにより不定な値が参照されうるという未定義動作を引き起こす[4]。これはconst修飾された静的ローカル変数にも当てはまるが、コンパイル時定数となるケースは該当しない。

その他 編集

伝統的にC言語では、コンパイル時定数はプリプロセッサ(cpp; en:C preprocessor)による置き換え(オブジェクト形式マクロ)で擬似的に実現されていた。C++ではconst修飾によるコンパイル時定数を用いることで、プリプロセッサによるマクロ定数をほとんどの場面で排除することができる。また、C++11以降ではconstexprのサポートにより、マクロ定数の必要性がさらに減っている。一方、C言語では、const修飾された変数がたとえコンパイル時定数であっても、C++よりも定数として利用可能な場面が制限される。C言語ではconst修飾された変数は外部リンケージを持つため、コンパイル時定数であってもヘッダーファイルに定数として直接定義して共有することはできない。一方、C++ではconst修飾された変数は既定で内部リンケージを持つため、ヘッダーファイルに定数として直接定義して共有することができる。固定長配列の要素数など、C言語では依然としてconst修飾によるコンパイル時定数が使えない場面も多く残っている。

なお、C言語では、列挙型enumにより定義された列挙定数の値が、内部的には整数定数であるため、マクロ定数の代わりに使われることがある。

用途 編集

再代入できない変数への代入は初期化時に限られ、後で別の値を誤って代入してしまうことを禁止できる。結果として、変数の使いまわしをなくすことができ、ソースコードの可読性や品質の向上にも寄与する。

コンパイル時定数はその値を実行時に変更することができない。つまり、プログラム上でデータに名前を付けて表したいが、コードによってその値を変更されては困るようなものに使用する。また、プログラム中の複数個所でそういったデータを繰り返し使用するケースにおいて、値を変更する必要が出てきたときは、定数の初期化部分を変更するだけで済むようになり、変更漏れを防止できる。

例えば、円周率を使用するプログラムでは、3.14...を定数PIとして定義しておけば、誤った値を代入する心配はない。また、円周率の精度を後から変更したとしても、初期化部分を変更しておけば、自然とプログラム全体で同一精度の値を使用するようになる。言語やソフトウェアフレームワークによっては、標準ライブラリに一部の数学定数を含むものもある。例えばJavaでは読み取り専用の静的フィールドによる倍精度浮動小数点数型の近似値 (public static final double) として、円周率java.lang.Math.PIおよび自然対数の底(ネイピア数java.lang.Math.Eが定義されている。.NET Frameworkでも同様に、それぞれSystem.Math.PIおよびSystem.Math.Eが定数フィールドとして定義されている。

脚注 編集

関連項目 編集