変数の初期化
(C言語における変数の初期化のわかりにくい点)

Copyright(C) 14May2003 coskx

C言語において同じ表現で,変数宣言の時と実行時で意味が異なることがあり,混乱の元となっている。

1.配列の宣言と初期化

配列を宣言する時の表現と,実行時に現れる要素を表す表現について,同じ表現が異なる意味になっている。
また宣言時には,値を配列に簡単に代入できるが,実行時に値を代入しようとすると面倒である。

int main()
{
    int ary1[10];              /*(1)*/
    int ary2[]={1,2,3,4,5,6};  /*(2)*/
    char str1[10]="ABCDEFG";   /*(3)*/
    char str2[]="ABCD";        /*(4)*/
    ary1[9]=123;               /*(5)*/
    ary2[]={4,5,6};            /*(6)この表現は誤り*/
    str1[10]="ABCDEFG";        /*(7)この表現は誤り*/
    str2[]="ABCD";             /*(8)この表現は誤り*/
    str1="ABCDEFG";            /*(9)この表現は誤り*/
    str2="ABCD";               /*(10)この表現は誤り*/
      :
    以下省略

この例で,変数宣言部は(1)〜(4)である。(5)〜(10)は実行部である。

(1)では次のような10この要素を持つ配列が定義される。配列ary1の第10要素を表しているわけではない。

ary1 (各要素はint型)

要素番号

0

1

2

3

4

5

6

7

8

9

未定

未定

未定

未定

未定

未定

未定

未定

未定

未定

ところが(5)では実行部なので要素番号9の要素に123を格納するだけである。要素数9個の配列ができるわけではない。

(2)の宣言ではコンパイラは自動的に,初期値の数に等しい要素数の配列を作る。

ary2 (各要素はint型)

要素番号

0

1

2

3

4

5

1

2

3

4

5

6

ところが,(6)は実行時なので,この表現は文法上許されない。
もしこの要求を実現するには,各要素に1つずつ代入するしかない。
ary[0]=4;
ary[1]=5;
ary[2]=6;

(3)では次のような10この要素を持つ配列が定義される

str1 (各要素はchar型)

要素番号

0

1

2

3

4

5

6

7

8

9

'A'

'B'

'C'

'D'

'E'

'F'

'G'

'\0'

未定

未定

ところが,(7)は実行時なので,この表現は文法上許されない。もしこの要求を実現するには,関数strcpy()を使って
strcpy(str1,"ABCDEFG")
とする。

(4)の宣言ではコンパイラは自動的に,初期値の数に等しい要素数の配列を作る。

str2 (各要素はchar型)

要素番号

0

1

2

3

4

'A'

'B'

'C'

'D'

'\0'

ところが,(8)は実行時なので,この表現は文法上許されない。もしこの要求を実現するには,関数strcpy()を使って
strcpy(str2,"ABCD")
とする。

(9)(10)もともに実行時なので,文法上許されないのでもしこの要求を実現するには,関数strcpy()を使うことになる。

2.構造体の宣言と初期化

構造体を宣言する時に,構造体の各メンバに値を代入するのは簡単だが,実行時に構造体の各メンバに値を代入するには,各メンバごとに行わなければならない。

typedef struct {
 int x;
 int y;
} point_t;

int main()
{
    point_t p1={5,10};              /*(1)*/
    point_t pa1[4];                 /*(2)*/
    point_t pa2[4]={{2,3},{5,6}};   /*(3)*/
    p1={5,10};                      /*(4)この表現は誤り*/
       :
    以下省略

この例で,変数宣言部は(1)〜(3)である。(4)は実行部である。

(1)ではp1.xに5が,p1.yに10が格納されたpoint_t型の変数p1が作られる。
(4)は実行時なので,この表現は文法上許されず,この要求を実現するには個別に代入し,次のように書く。
 ( p1.x=5; p1.y=10; )

(2)(3)は配列の場合であり,(3)は最初の2つの要素が初期化される。

pa1 (各要素はpoint_t型)

要素番号

0

1

2

3

メンバx メンバy

未定

未定

メンバx メンバy

未定

未定

メンバx メンバy

未定

未定

メンバx メンバy

未定

未定

pa2 (各要素はpoint_t型)

要素番号

0

1

2

3

メンバx メンバy

2

3

メンバx メンバy

5

6

メンバx メンバy

未定

未定

メンバx メンバy

未定

未定

 

3.ポインタ変数の宣言と初期化

ポインタ変数を宣言する際に,値を代入する初期化を行う時の記述が,実行時にポインタ変数に値を代入する記述と異なるので混乱しやすい。

int main()
{
    int x=123;
    int y;
    int *p1;            /*(1)*/
    int *p2=&x;         /*(2)*/
    p1=&y;              /*(3)*/
    *p1=x;              /*(4)*/
       :
    *p1=&y;             /*(5)この表現は誤り*/
       :
    以下省略

この例で,変数宣言部は〜(2)である。(3)〜は実行部である。

(1)「int *p1」は「p1は,int型変数のアドレスを格納することができるポインタ変数である」の意味である。

(3)「p1=&y」は(1)で宣言したint型変数のアドレスを格納することができるポインタ変数p1にint型変数yのアドレスを代入している。

(4)「*p1=x」は,変数yにxの値を代入していることになる。なぜなら,p1に変数yのアドレスが格納されているため,*p1は変数yを指しているからである。(*p1はポインタ変数p1の保持しているアドレスにあるint型変数を意味する)

次の2つが紛らわしいのでよく味わうこと

(2)「int *p2」ではint型変数のアドレスを格納することができるポインタ変数p2を確保し,ポインタ変数p2に変数xのアドレスを格納するところまで行う,変数宣言+変数初期化を表している。ポインタ変数p2に保存されているアドレスの位置にあるint型変数(p2が指しているint型変数)にxのアドレスを格納しているわけではない。

(5)「*p1=&y」は,表現が(2)と同じだが実行部にあるため「ポインタ変数p1に保存されているアドレスの位置にあるint型変数(p1が指しているint型変数)」にyのアドレスを格納する」という意味になり,文法上許されない記述である。