[前へ]   [目次へ]   [次へ]

変数の宣言と定義(C/C++)


   (04/10/30) 一部表現が曖昧だった部分を修正
   (04/11/29) 配列の初期化に関する部分を修正
      修正箇所2
   (05/08/26) 型名の基本型にboolが入っていなかった部分を修正
   (08/10/17) volatileについて加筆


C/C++言語では変数を使う前に宣言および定義を行わなければなりません。

定義を行えば宣言も兼ねるため、変数は通常定義のみを行います。
宣言のみを行うことは少し特別なケースになります。

さて、変数の宣言と定義はほぼ同じ構文です。
構文は以下のような感じです。

装飾1 型名 装飾2 名前 装飾3 初期化式

このうち、必須なのは「型名」と「名前」のみで、他は必要ない場合は省略することができます。
宣言のみを行う場合は上記構文の前に「 extern 」を置きます。

前項で基本型の一覧 を示しましたが、
あの表に数多く出てきた「 signed 」と「 unsigned 」は装飾1に入ります。
また、厳密には「 short 」「 long 」も装飾1ですが、
それは気にしなくても問題ないので、これらは基本型として分類します。



例として、 int 型の変数「 test 」を定義するには、以下のように記述します。

int test;

構成は、装飾1、なし、型名、「 int 」、装飾2、なし、変数名、「 test 」、装飾3、なし、初期化式、なしです。

C/C++言語では、定義したばかりの変数は基本的に何が入っているかわからないことになっています(一部例外あり)。
なので、 使う前には初期値を代入しなければいけません。

また、C言語では、 変数はブロックの先頭で宣言および定義しなければいけません。
C++言語ではこの制限は無くなっており、任意の部分で宣言および定義ができるようになっています。

C/C++言語ともに、初期化式の次に ; ではなく , を使うと、
装飾1、型名が同じ変数を、装飾2以降を記述するだけで定義できます。

例として、 int 型の変数「 test 」と「 test2 」を一度に定義するには、以下のように記述します。

int test,test2;

では、構文上の各意味について解説します。

長いのでページ内リンクです(笑)
   装飾1
   型名
   装飾2
   名前
   装飾3
   初期化式

まず、装飾1の部分です。
この部分には変数の特性などを示す項目が主に入ります。
この中には装飾2にあたる部分に記述してもいいものがありますが、
先に書くことが多いことと、意味が変わるものがあるので、基本的に装飾1の部分に書くことを私は推奨します。

この部分に利用可能な項目は以下の通りです。
意味がかぶるもの以外は複数指定することができます。

 (10/10/17)volatileについて加筆
キーワード効果
auto変数の寿命を指定します。
autoが指定された変数は定義されたブロックが終了すると消滅します。
この寿命は何も指定していない時と同じであり、書く意味は多分ありません。(笑)
実際、見たことありません(笑)
static変数の寿命を指定します。
staticが指定された変数はプログラム全体の終了まで維持されます。
初期化式を記述している場合は始めて定義行を通過した時のみ初期化され、2度目以降は無視されます。
const変数へのアクセスを指定します。
constが指定された変数は読み取り専用になり、内容を変更することが禁止されます。(初期化は可能)
また、ポインタ型の場合は参照先が読み取り専用になり、保持しているアドレスは変更できます。
signed変数の内部表現と保持範囲を指定します。
signedが指定された変数は最上位ビットを正負を表すフラグとして扱います。
多くの環境では省略時もsignedとして扱うので、char型以外は多くの場合、指定は不要です。
unsigned変数の内部表現と保持範囲を指定します。
unsignedが指定された変数は全てのビットを値として扱います。
負数を保持することができなくなりますが、その代わり正数を2倍保持できます。
-1には絶対にならなくなるので、unsigned変数との比較条件に「0以上」とか書いちゃダメです。条件になってません(笑)
register変数の確保方法について指定します。
registerが指定された変数は実行時にできる限り高速に参照できるように配置するよう要求されます。
基本的にはレジスタへの確保を試みますが、レジスタの数は多くないため、できなかった場合は通常と同じです。
また、register変数はポインタ値(アドレス)を得られません。
volatile変数の扱いについて指定します。
volatileが指定された変数はコンパイラが感知できない方法で値が変更されるかもしれないことを宣言します。
複数のスレッド等が同じ変数を共有するような、高度な並列処理を行う場合に使用します。
struct変数の型名について指定します。
structが指定された変数の型名は「構造体タグ名」を指定することを宣言します。
C++では構造体タグ名を型名にする場合でもこの指定は不要です。
union変数の型名について指定します。
unionが指定された変数の型名は「共用体タグ名」を指定することを宣言します。
C++では共用体タグ名を型名にする場合でもこの指定は不要です。
enum変数の型名について指定します。
enumが指定された変数の型名は「列挙型タグ名」を指定することを宣言します。
C++では列挙型タグ名を型名にする場合でもこの指定は不要です。

次に、型名についてです。

 (05/08/26)型名の基本型にbool型が入っていなかった部分を修正

型名には、基本型( bool,char,short,int,long,float,double,long double )、
void (ポインタ型にする場合のみ)、
typedef (型に別名を付ける)された型、
構造体、共用体、列挙型のタグ名(Cでは指定が必要)、
クラス(C++のみ)が使用できます。

最初は基本型のみ覚えておけばいいでしょう。


次に、装飾2についてです。
装飾2では、ポインタ型、参照型(C++のみ)を指定できます。

ポインタ型にする場合は * を、参照型にする場合は & を使います。

ポインタ型は * を何個も書くことにより、「へのポインタ」を何個も付加できます。
ただ、3個以上繋げるのはどうかと(笑)

参照型の場合は必ず目標を指定しなければいけません。

最初はどちらも使用することはあまりないはずなので、後回しでもいいでしょう。


次に、名前についてです。
名前は、次の規則の範囲で、自由につけることができます。

●名前は半角のアルファベット、数字、_(アンダースコア)のみで構成されます。
●名前の先頭1文字には数字が使えません。
●同一スコープ(ブロック)内に既に存在する名前は付けられません。
●大文字と小文字は区別され、別のものとして扱われます。
●キーワード、関数名、型名などと重複することはできません。
●名前の長さは31文字以内でなければいけません。(環境によってはもっと多くの文字を使えるかもしれません)

上記規則の範囲であれば「 a 」でも「 b 」でもいいわけですが、
これだと後々意味不明になります。(プログラムが小規模なうちはこれでもやってられますが・・・)

そのため、通常名前にはその変数が保存する情報について簡単に示す名前が使用されます。
また、命名については通常英単語か、英単語を省略したものがよく使われます。
例えば、回数を数えるカウンターの場合は「 count 」や「 cnt 」などが使われます。

一部の名前には慣習的な意味があります。
私が知っているものは関係する部分の解説時に示します・・・が、
その辺はあまり知らないのであまり期待しないでください(汗)


次に、装飾3についてです。 
この部分では、配列であることを指定することができます。

配列を指定するには、大カッコ( [ ] )の中に要素数を書きます。
この要素数はコンパイル時に確定できなければいけないので、定数しか使えません。

また、 [ ] は複数繋げることができます。
複数繋げた場合は全ての要素数を掛けた分の領域が確保されます。

 (04/10/30)表現が曖昧だったので修正

また、初期化を行う場合は一番左の [ ] に限り要素数を省略できます。

 (04/10/30)修正ここまで

配列については、近々書く予定です。


最後に、初期化式についてです。
この部分では、変数が定義された時に初めから入っている値(初期値)を指定できます。
また、この式を使用して初期値を与えることを「初期化」と呼びます。

初期化式は、「 = 」を付けて指定します。
また、初期化式には型によっていくつかの記述方法があります。

●基本型の場合
基本型の場合は=の後ろに普通に値を続けます。

例: int 型の変数「 test 」を定義し、初期値を10にするには以下のように記述します。

int test=10;

●配列の場合
配列の場合は、その配列全ての要素を一斉に初期化することができます。
まず、「 { } 」(中カッコ)を書き、その中に「 , 」(カンマ)で区切って値を記述します。
左に書いたものから順に要素0から代入されていきます。

この時、宣言した要素全てを書き込まなくても構いません。
その場合は、要素0から記述された番号までが初期化されます。

 (04/11/29)追加

また、初期化を省略された要素は0で埋められます。

(04/11/29) 追加ここまで

宣言した要素数以上書き込むとエラーになります。

例: int 型の配列「 test 」(要素数5)を定義し、
初期値を [0]=2、[1]=4、[2]=6、[3]=8、[4]=10 にするには以下のように記述します。

int test[5]={2,4,6,8,10};

●多次元配列の場合
多次元配列の場合も、配列の時と同じように初期化できます。
この場合は、 { } を重ねて配置して、初期化する要素を示します。

例: int 型の配列(要素数3)の配列「 test 」(要素数5)を定義し、初期値を
[0][0]=0、[0][1]=1、[0][2]=2、[1][0]=3、[1][1]=4、[1][2]=5、[2][0]=6、[2][1]=7、[2][2]=8、
[3][0]=9、[3][1]=10、[3][2]=11、[4][0]=12、[4][1]=13、[4][2]=14
 にするには以下のように記述します。
(長いうえにややこしい・・・(笑))

int test[5][3]={{0,1,2},{3,4,5},{6,7,8},{9,10,11},{12,13,14}};

●char配列の例外(リテラル文字列による初期化)
初期化する際に初期化される配列がchar型の配列の場合、
リテラル文字列( "" で囲まれた文字列)で初期化できます。

この方法の場合、初期化される文字列はナル終端文字列になり、
表記上の文字数(バイト数)+1個分の容量が必要です。

 (04/11/29)修正

また、ナル文字以降の要素がある場合は0で埋められます。

(04/11/29)修正ここまで

この初期化は文字列として使用する場合に便利です。

例: char 型の配列「 test 」(要素数10)を定義し、初期値を文字列 "test" にするには以下のように記述します。

char test[10]="test";

●構造体の場合
構造体の場合も、
まず、「 { } 」(中カッコ)を書き、その中に「 , 」(カンマ)で区切って値を記述します。
構造体の定義で最初(ファイル先頭側)に定義された方から順に初期化できます。
構造体内にchar配列が存在する場合は上記のリテラル初期化も可能です。

例:
構造体定義
struct abc{
   int a;
   char b[10];
   int c;
};

上記の定義を持つ構造体があるとき、上記構造体型の変数「 test 」を定義し、
初期値を a=10 、 b="abc" 、 c=20 にするには以下のように記述します。

struct abc test={10,"abc",20};


今回は変数の宣言と定義について大半の要素を網羅したので、
随分長くなってしまいました。

今はとりあえず基本型の部分だけ理解していただければOKです。
それ以外の部分は「そういうのもあるんだ」ぐらいで(笑)
(基本型以外の部分は各要素の解説時にもまた書くつもりです。)

[前へ]   [目次へ]   [次へ]

プログラミング講座 総合目次

最終更新 2008/10/17