第8章 初期化

8.1 初期化のタイミング

初期化のタイミングには2通りあります。

ローカル変数は、定義されている関数あるいはブロックに入るごとに初期化されます。

一方、グローバル変数(や関数内宣言のstatic付き変数)、つまり静的記憶区間をもつオブジェクトは、

そのプログラムの開始時に一度だけ行われます。

8.2 暗黙の初期化(自動初期化)

初期化を行わなかった場合、グローバル変数は暗黙のうちにゼロ初期化を行ないます。

つまり、整数は0に、浮動小数点数は0.0に、ポインタは0になります。

ローカル変数の場合はゴミ値になります。

ローカル変数は、それがローカルメモリ上に確保されたとき、たまたまそこにあった値が使われることになります。

通常はコンパイル時に警告されます。

int i;// 0になる

double d; // 0.0になる

int* p; // 0になる

int main() {

 int ii; // ゴミ値になる

 double dd; // ゴミ値になる

 int* pp; // ゴミ値になる

 cout << i << 'n'; // 正しい

 cout << ii << 'n'; // 警告される

}

8.3 初期化の方法

/* 単純変数の初期化 */

単純型名を使った変数の初期化は次のように記述します。

(1)int dt = 100; // 代入形式の初期化

(2)int dt(100); // 関数形式の初期化

C++では新しく(2)の関数形式も可能になりました。

C++の初期化はCより拡張されています。

Cでは以下の例で(*)の付いた初期化だけが可能です。

Cではグローバル変数の初期化は定数式を用いる必要がありました。

int a = 100; // (*)リテラルによる初期化

int b(100); // 同、関数スタイル

int c = a; // 宣言済み変数による初期化

int d(a); // 同、関数スタイル

int e = a+2*10; // 計算式による初期化

double f = sqrt(10,9); // 関数による初期化

void func() {

 int aa = 100; // (*)

 int bb (100); //

 int cc = a; // (*)

 int dd(aa); // 

 int ee = aa+2*10; // (*)

 double ff = sqrt(10,0); // (*)

 static int gg = 200; // (*)static変数のリテラルによる初期化

 static int hh = aa; // static変数の非定数での初期化

}

/* constにともなう初期化 */

constタイプのオブジェクトの初期化は次のようにします。

この値はあとで再設定することはできません。

int dt1 = 300;

const int dt3 = 300; // dt3の値を300にする

const int* p = &dt1; // ポインタpを変数dt1のアドレスにする

また関数の仮引数が、

void func(const char* p);

のようになっているときは、関数呼び出し時に実引数が、const仮引数に設定されます。

/* static付きローカル変数の初期化 */

関数内で宣言されるstatic付き変数の初期化は、その関数が最初に呼ばれたときの値を設定します。

2回目以降に呼ばれたときは、その初期化記述は意味がありません。

void func() {

 static int n = 0; // 最初に呼ばれたときに0になる

 ++n; // nに対する変更を維持する

}

/* 配列の初期化 */

int dt[4] = {10, 20, 30, 40}; // 初期値は10,20,30,40になる

int dt[4] = {10, 20, 30}; // 初期値は10,20,30,0になる

int dt[] = {10, 20, 30, 40}; // 配列長は4になる

int dt[4] = {10, 20, 30, 40, 50}; // 初期化子が多すぎるのでエラー

int bigary[1000] = [0]; // 配列内容をすべて0にする

/* 多次元配列の初期化 */

int aa[2][4] = {10, 20, 30, 40, 50, 60, 70, 80};

int aa[2][4] = {{10, 20, 30, 40}, {50, 60, 70, 80}};

aa[0][0] = 10

aa[0][1] = 20

aa[0][2] = 30

aa[0][3] = 40

aa[1][0] = 50

aa[1][1] = 60

aa[1][2] = 70

aa[1][3] = 80

int bb[2][4] = {{10, 20}, {50, 60, 70}}; // この記述は

int aa[2][4] = {{10, 20, 0, 0}, {50, 60, 70, 0}}; // このように解釈される

/* 多次元配列先頭サイズの省略 */

int bb[][3] = {10, 20, 30, 40, 50, 60}; // この記述は

int aa[2][3] = {10, 20, 30, 40, 50, 60}; // このように解釈される

int aa[2][] = {10, 20, 30, 40, 50, 60}; // NG。先頭添字だけ省略可能

/* 美化のための最終カンマの許可 */

int dtc[3][3] = {

 10, 20, 30,

 40, 50, 60,

 70, 80, 90 ←最後のカンマがないのできれいでない

};

int dtc[3][3] = {

 10, 20, 30,

 40, 50, 60,

 70, 80, 90, ←最後のカンマを付けてもよい

};

/* 文字配列の初期化 */

char ss[80] = "これは文字列です";

char ss[] = "abcd"; // この記述は

char ss[5] = "abcd"; // こう解釈される。末尾の'0'を入れて5

文字配列は単にchar型の配列ですから、次のように文字コードで初期化することもできます。

次の3つの初期化の結果は同じです。

char ss[5] = "ABCD"; // 通常の初期化

char ss[5] = {'A', 'B', 'C', 'D', '0'}; // 文字定数で初期化

char ss[5] = {65, 66, 67, 68, 0}; // 65は'A'の文字コード

/* 2次元の文字配列の設定 */

char days[4][10] = {"Sunday", "Monday", "Tuesday", "Wednesday"};

cout << days[1] << 'n'; // 出力は"Monday"

/* string型の初期化 */

string ss = "これは文字列です"; // 単純初期化

string str[2] = {"文字列a", "文字列b"}; // 配列の初期化

/* ポインタの初期化 */

int dt;

int *p = &dt; // &dt(変数dtのアドレス)が設定される

char ss[80];

char *p = ss; // ssはそれ自身アドレスなので&ssとする必要はない

/* 参照の初期化 */

ポインタのように'&'演算子を使う必要はありません。

また参照の設定はconstと同じように、初期化(や関数の仮引数への設定)のタイミングだけで行なうことができます。

あとで変更することはできません。

int a;

int &r = a; // 参照であるrを宣言しaのアドレスを設定。&aとする必要はない。

/* クラスの初期化 */

クラスの初期化はコンストラクタで行ないます。

次のようなクラスがあったとします。

コンストラクタが多重定義されています。

class Mycls {

 int x,y;

public:

 Mycls() { x = 0; y = 0; }

 Mycls(int n1) { x = n1; y = 0; }

 Mycls (int n1, int n2) { x = n1, y = n2; }

 void disp() { cout << "x=" << x << " y=" << y << 'n'; }

};

このクラスは次のような多彩な初期化が可能です。

Mycls d1; // 引数なし

Mycls d2 = Mycls(); // この記述の別表現が上のものになる

Mycls d3(10); // xだけ指定

Mycls d4 = Mycls(10); // この記述の別表現が上のものになる

Mycls d5 = 10; // 引数1個のときは代入形式で指定可

Mycls d6(30, 40); // x,yを指定

Mycls d7 = Mycls(30, 40); // この記述の別表現が上のものになる

Mycls d8 = d7; // 宣言済みのオブジェクトで初期化

Mycls d9[2] = { Mycls(50, 55), Mycls(60, 65) }; // 配列の初期化

/* 構造体と共用体の初期化 */

C++では、構造体や共用体はクラスの一部なので、初期化方法も通常のクラスと同じです。

ただしC互換の構造体や共用体の場合は、コンストラクタがないので、C由来のスタイルになります。

次に例を示します。

C互換構造体の初期化例

struct Sdata {

 int x;

 int y;

};

Sdata pos = { 640, 400}; // pos.xを640に、pos.yを400に設定する

C互換共用体の初期化

union Udata {

 int id;

 char ch;

};

Udata dbuf = { 1000 }; // 先頭メンバであるdbuf.ifを1000に設定する