プログラムが計算やユーザーなどの外部からの入力で得た情報は、メモリに保存しなければなりません。 バイナリデータをメモリに保存したり、保存したデータを計算などで利用するには変数を使います。 D 言語でも、変数の概念は変わりません。
変数とは、記録装置の特定の部分を文字表す識別子のようなものだと考えられます。 本来、機械語ではデータを保存するメモリの位置はアドレスという数値情報で指定しなければなりません。 C 言語のポインタを理解しているか、アセンブリ言語の経験者であれば、その原理は理解しているはずです。
新しいデータをメモリに保存するには、変数宣言文を用いて変数を宣言しなければなりません。 変数宣言文は必要なメモリを割り当てます。 変数宣言が行われる手前では、コンパイラがその識別子を認識することはできません。 変数は、宣言して初めて識別氏が登録され、利用することができるようになります。
型 変数名1 , 変数名2 , 変数名 ... ;
このように、変数宣言は変数の型に続いて、変数の識別子を指定します。 変数は、同一の型であればカンマ , で区切ることで、任意の数を同時に宣言することができます。
変数には、保存するデータの種類に応じて適切な型を与えなければなりません。 保存するデータの種類とは、整数、浮動小数点数、文字など、データの用途によって異なります。 C 言語では char や int などの型が存在しましたが、D 言語はさらに新しい型が加えられています。
型 | 解説 |
---|---|
void | 型なし |
bit | 1bit |
byte | 符号付き 8bit 整数 |
ubyte | 符号なし 8bit 整数 |
short | 符号付き 16bit 整数 |
ushort | 符号なし 16bit 整数 |
int | 符号付き 32bit 整数 |
uint | 符号なし 32bit 整数 |
long | 符号付き 64bit 整数 |
ulong | 符号なし 64bit 整数 |
cent | 符号付き 128bit 整数 (将来のために予約) |
ucent | 符号なし 128bit 整数 (将来のために予約) |
float | 32bit 浮動小数点数 |
double | 64bit 浮動小数点数 |
real | 最大の浮動小数点数 (機種依存) |
ireal | 虚数型の浮動小数点数 |
ifloat | float虚数 |
idouble | double虚数 |
creal | 二つの浮動小数点数からなる、複素数 |
cfloat | float複素数 |
cdouble | double複素数 |
char | 符号なし 8 bit UTF-8 文字 |
wchar | 符号なし 16bit UTF-16 文字 |
dchar | 符号なし 32bit UTF-32 文字 |
整数型の変数には、それぞれのサイズで符号付きと符号無しが存在します。 例えば byte 型の変数は 8 ビットなので、すべてのビットを使えば 255 までの値を表現することができますが、負数を表現する場合は最上位ビットを符合用に利用するため -128 〜 127 までの範囲となります。
C 言語プログラマであれば、驚くほど多くの型が追加されているので戸惑うかもしれません。 しかし、型が多く見られる原因の 1 つは、C 言語では変数が符号を付きかどうかを指定する方法が、型ではなく修飾子であったことに対して、D 言語では符号付きと符号無しを別の型として定義していることにあります。
宣言した変数に値を保存するには、代入演算子 = を用いて値を代入します。
変数名 = 式文
代入演算子は、必ず左オペランドに変数を指定しなければなりません。 変数に代入する値を返す式文は、単純なリテラルでも、複雑な計算式や関数の戻り値でもかまいません。 ただし、式文の結果は変数の型と互換性がなければなりません。
変数に保存した値は、式文のオペランドに変数名を指定することで利用することができます。 代入演算の代入対象として用いられた場合は保存を表し、それ以外であれば変数に保存されている値にアクセスすることを表しているのです。
import std.stream; int main() { int value1 , value2; value1 = 100; value2 = 1000; stdout.printf("value1 = %d\n" , value1); stdout.printf("value2 = %d\n" , value2); return 0; }
このプログラムでは、32 ビット符号付き整数の int 型変数 value1 と value2 を宣言しています。 宣言した変数には、それぞれ = 演算子で値を代入して初期化し、代入した値が適切に保存されているかどうかを確かめるため、printf() 関数で変数にアクセスし、値を表示してます。
変数宣言文では、変数の宣言と同時に変数の値を初期化する変数初期化子を指定することができます。 変数の初期化を行う場合は、変数名の宣言に続いて = 記号、そして式文を指定します。
型 変数名1 = 式文 , ... ;
複数の変数を同時に宣言する場合、初期化する変数と初期化しない変数を混ぜることは可能です。 因みに、宣言だけで何の値も代入していない場合、C 言語では不確定な値が格納されていましたが、D 言語は明確にデフォルトの値が格納されていると定められています。 通常、整数型の変数のデフォルトの値は 0 です。
import std.stream; int main() { int value1 = 100 , value2; stdout.printf("value1 = %d\n" , value1); stdout.printf("value2 = %d\n" , value2); return 0; }
このプログラムでは、value1 変数は宣言と同時に初期化していますが、value2 は初期化していません。 これらの値を表示すると、value1 は初期化した値 100 が保存されていて、value2 にはデフォルトの値 0 が格納されていることを確認することができます。
変数に型が存在するように、リテラルにも型が存在します。 変数や式にリテラルを指定する場合も、リテラルの型を意識しなければなりません。
リテラルの型は単純で 10 は整数型、1.0 は浮動小数点数形であることがわかります。 しかし、10 が int 型なのか、long 型なのか、1.0 が float 型なのか、double 型なのかを判断することはできません。 そこで、リテラルの型を明示的に表現するには、リテラルの末尾に 1 文字の接尾辞を使います。
整数リテラルの場合、l または L を用いて long であることをアピールし、u または U を用いて 符号無しであることをアピールすることができます。 l または L と u または U の接尾辞は同時に指定することも可能です。 このとき、順番や、大文字と小文字の組み合わせは自由です。 この接尾辞は、コンパイラにリテラルの型を伝えるためのもので、実行時に評価されるものではありません。
整数の型は、接尾辞が指定されていない場合は ulong、long、int から表現可能な最小の方を選択します。 ただし、10進整数でない場合は int、uint、long、ulong の優先順位で、表現可能な型が選択されます。
import std.stream; int main() { long value1 = 1000L; ulong value2 = 10000UL; stdout.printf("value1 = %d\n" , value1); stdout.printf("value2 = %d\n" , value2); return 0; }
このプログラムの value1 と value2 の変数に初期化する値を見てください。 long 型の変数を初期化するリテラル 1000 には接尾辞として L が指定されています。 同様に、value2 を初期化するリテラル 10000 にも ulong であることを表す UL を指定しています。
因みに、算術演算のオペランドの型が異なる場合、最も大きいサイズの型に昇格されます。 int 型と long 型のオペランドの計算結果は long 型に、整数型と浮動小数点数型の計算結果は浮動小数点数型が優先されます。
import std.stream; int main() { stdout.printf("10 + 5.555 = %g\n" , 10 + 5.555); return 0; }
このプログラムを実行すれば、整数と浮動小数点数の演算結果が浮動小数点数型として返されていることが証明できます。
浮動小数点数リテラルの型を表現するには、やはり整数リテラル同様に接尾辞を利用します。 浮動小数点数はデフォルトで double 型として認識されますが、float 型を表現したい場合は f または F を指定します。
import std.stream; int main() { float f = 3.4E38F; double d = 1.7E308; stdout.printf("f = %g\nd = %g\n" , f , d); return 0; }
このプログラムの float 型変数 f を初期化しているリテラルは、接尾辞に F が指定されているため、リテラルは float 型として処理されます。
C、C++、C#、Java のいずれの言語にも存在しなかった新しい型が存在します。 この新しい拡張は、D 言語が浮動小数点数の扱いに積極的であることが伺えます。
まず、real 型についてですが、real 型は D 言語をコンパイルしたコンピュータの実装が提供する最大サイズの浮動小数点数型であると定められています。 これは、Intel の CPU などで 80 ビットの浮動小数点数がサポートされているため、こうした CPU 依存の浮動小数点数型変数を扱えるように対応したものです。 浮動小数点数演算用のレジスタなどが用意されている CPU では、real 型を使ったほうが正確な結果を得ることができます。 浮動小数点数リテラルが real 型であることをアピールするには、接尾辞に l または L を指定してください。
そして、注目すべきは ireal、ifloat、idouble 等の虚数型と、creal、cfloat、cdouble などの複素数型です。 虚数や複素数については、多くの方が高校の数学で学習しているはずです。 虚数とは、2 乗して負になる数のことで、特に 2 乗して -1 になる数を虚数単位 i として表します。 複素数とは、虚数単位 i を含む 2 つの実装 a + bi の形で表現される数を指します。
従来の言語でも、複素数を表現するためのマクロやテンプレート、クラスというものは用意されました。 しかし、D 言語は言語レベルでこれをサポートしているため、より自然な記述でプログラミングすることができます。 コンパイラが虚数や複素数の意味を理解できるという点も重要です。 浮動小数点数リテラルが虚数であることをアピールするには、リテラルの接尾辞に i または I を指定してください。
コンパイラは虚数を認識することができるため、明示的に複素数を表す必要はありません。 実数と虚数の演算が認められれば、コンパイラはそれを複素数と認識することができるのです。
import std.stream; int main() { idouble iDouble = 1.3i; cdouble cDouble1 = (4 + 3i) , cDouble2 = (4 - 3i); stdout.printf("iDouble = %g\n" , iDouble); stdout.printf("cDboule1 * cDouble2 = %g\n" , cDouble1 * cDouble2); return 0; }
iDouble を表示すると 1.3 という結果が得られますが、この変数の型が idouble 型であることから、この変数が保存する値が虚数であるということを認識することができます。 一方、複素数を保存する cdouble 型の変数 cDboule1 と cDouble2 を初期化しているのは複素数 a + bi と a - bi です。 これらの複素数は互いに共役な複素数なので、これらの積は実数 25 となるでしょう。
虚数や複素数の原理については、改めて高校数学U + B を学習してください。