Java フロクラミングリファレンス 詳説 JDK 解体新 変数のスコープの内側では , 「 java. awt. ima ge. BufferedImage. 識別子」という形式は , ローカル変数「 java 」に対するフィールドア クセスとして解釈される。 このようになる理由は , Fig. 6 に示した ルールによる。「 java. awt. image. Buffered lmage. TYPE_3BYTE_BGR 」なるトークン の並びは , Java 処理系によっていったん 「曖味名」であると解釈される。そしてそれ を Fig. 6 に示したルールによって再解釈す る。ここで Fig. 6 をよく見ると , ピリオド 「 . 」で結ばれた識別子の並びに対しては ( 1 ) のルールが再帰的に適用され , 最終的には 先頭の単純名 ( 上記例では「 java 」 ) をどのよ うに再解釈するかがいちばんのポイントに なることがわかる。その部分の再解釈にお いて , 「 java 」というローカル変数のスコー プ内 ( List4 では ( e ) のケース ) であれば , ( 2- 1 ) によって「 java 」という単純名は「式名」と 再分類される。いったん「式名」に分類され ると , あとはピリオドで連結された部分に 対してつぎつぎに ( 1-3 ) のルールが適用さ れ , 全体としても「式名」に再分類される。 それに対して , 「 java 」という名前がロー カル変数として宣言されていない場合 ( List 4 の ( c ) , ( d ) ) には ( 2-7 ) が適用されて , の単純名は「パッケージ名」と分類される。 あとはピリオドで連結された部分に対して つぎつぎに ( 1-1-2 ) のルールが適用されてい くが , 最後の識別子ひとつ手前の「 Buffered lmage. 」は「型」になるため , ( 1-1-1 ) のルー ルが適用され , 「 java. awt. image. BufferedIm age 」は「型名」に再分類される。そして次 のピリオドのあとにある「 TYPE_USHORT _GRAY 」または「 TYPE_3BYTE_BGR 」を付 加した曖味名に対しては , 「 . 」の左側が「型 名」に再分類されたため , ( 1-2 ) が適用され て「式名」と再分類されるのである。 一方 , 最後の ( f ) の例が示すように , ロ ーカル変数「 java 」が宣言されていたとして も , 「 java. awt. image. BufferedImage 」をクラ ス名として用いることはできる。これは構 曖昧名 . 識別子 曖昧名 . 識別子 曖昧名 . 識別子 Fig. 6 Fig. 5 名前 : 式名 . 名前と式名の構文 バッケージ名 型名 式名 メソッド名 識別子 曖昧名 曖昧名を解釈するルール IS List 4 の変更例 / * 虻 4 の ( e ) の用例は以下に置き替えてもコンパイルできる。 * それに対して , (c ). ( d ) ではカッコを挿入することは許されない * / system. 0uし . println( ( ( ( (java) . a 響し ) . image). BufferedImage) . TYPE-3BYTE—BGR); ノ * ただし ( e ) の用例でも , 次のようにカッコを用いてはならない。 * / / * NG: ピリオドがカッコの内側に入っている。 * / system. out.println( ( ( ( ( java. )awt) . image) . BufferedImage) .TYPE—3BYTE—BGR); / * NG: ピリオドの右側の識別子をカッコで囲んでいる。 * / System ・ 0uに . println( ( ( ( ( java) . (awt) ) . image) . BufferedImage) . TYPE-3BYTE—BGR); ( 1 ) 曖昧名が冂を含む被修飾名であれば , 以下に従う ( 1 -1 ) 冂の左側がバッケージ名に再分類されたならば , 以下に従う ( 1 -1 -1 ) 冂の左側のバッケージ名が存在し , 該当バッケージに「 . 」の右側の名前を持つ型が定 義されていれば , この曖昧名は型名と再分類する ( 1 -1 -2 ) さもなければ , この曖昧名はパッケージ名と再分類される ( 1 -2 ) 「」の左側が型名に再分類されたならば , この曖昧名は式名と再分類される ( 1 -3 ) 「」の左側が式名に再分類されたならば , この曖昧名は式名と再分類される ( 2 ) 曖昧名が単一の識別子からなる単純名であれば , 以下に従う ( 2-1 ) 該当識別子と同じ名前のローカル変数宣言あるいは引数宣言のスコープ内であれば , この曖昧 型インポート宣言によって宣言されていたら , コンバイルエラーである ( 2-6 ) さもなければ , 当該コンバイル単位において , 該当識別子と同名の型が複数のオンデマンド・ マンド・型インボート宣言によって宣言されていたら , この曖昧名は型名に再分類される ( 2 ー 5 ) さもなければ . 当該コンバイル単位において , 該当識別子と同名の型が正確にひとつのオンデ いて , 該当識別子と同名の型が宣言されていたら , この曖昧名は型名に再分類される ( 2-4 ) さもなければ . 当該コンバイル単位が含まれるパッケージに所属する別のコンパイ丿レ単位にお は型名に分類される あるいはクラスまたはインタフェイス宣言によって宣言されているならば , この曖昧名 ( 2-3 ) さもなければ , 当該コンバイル単位において , 該当識別子と同名の型が , 単独・型インボート ラスあるいはインタフェイスがあれば , この曖昧名は式名に分類される ( 2-2 ) さもなければ , 該当識別子と同名のフィールドを宣言しているか , あるいは継承している , ク 名は式名に再分類される ( 2-7 ) さもなければ , この曖昧名はパッケージ名と再分類される 文情報を用いたためである。つまり (f) の 部分はローカル変数の宣言において型が記 されるべき場所であるため , そこに指定さ れた名前は「型名」と解釈されるのだ。 こうした複雑な解釈ルールが必要になる のは , パッケージ名の階層的な修飾を区切 るための記号と , フィールド名やメソッド 名の修飾を区切るための記号にすべてピリ オド「 . 」を用いたために生じた曖味さであ る。たしかに List 4 のように意図的に混乱 させるようなコードを記すと , この問題は 深刻なものに思われがちだが , 実際にはコ ンパイラの立場からは曖味さは生じない し , またこうしたソースの字面上の曖味さ が問題になるケースはまれであろう。それ よりは , 区切り記号が常に「 . 」で統一され ていることは , むしろソースの記述が楽に なる利点のほうが大きいと思われる。 Java プログラミングリファレンス詳説 JDK 解体新書 97
と ( 初期化を含めた ) 変数宣言を , union CharOrDoubIe { char c; double d; と一緒に行ってもかまいません。 配列型 構造体型はおもに異なる型の変数の集合 メンバ変数の型名 n メンバ変数名 n , メンバ変数の型名 1 メンバ変数名 1 . メンバ変数の型名 0 メンバ変数名 0 : く書式 > struct 構造体タグ名 { ( 1 ) 構造体型の定義の仕方 Fig. 6 構造体型 struct 構造体タグ名 " の部分が型名となる く注意 > ・メンバは 1 個以上なくてはならない st 「 uct CompIexNumber { く例 > 実数部と虚数部を持つ複素数型 / * 虚数部 * / / * 実数部 * / st 「 uct 構造体タグ名構造体型変数名 メンバ n の初期値 メンバ 1 の初期値 , メンバ 0 の初期値 , く書式 > struct 構造体タグ名構造体型変数名 , ( 2 ) 構造体型変数の宣言の仕方 double image; double real; 42 C MAGAZINE 2000 5 C. iamge = C. image * k; = c. 「 e 引 * k; C. 「 e 引 < 例 > 複素数 c の大きさを k 倍する < 書式 > 構造体型変数名 . メンバ名 ( 3 ) 構造体メンバの参照 struct ComplexNumber i= { 00 1 .0 } : < 例 > 虚数単位 i = 同型の構造体変数または定数 : として使いたいなどというときです。この ような目的に使用できるのが共用体型 ( unio n type) です。共用体型の定義と共用体型 変数の宣言の仕方を Fig. 7 に示します。共 用体も列挙型と構造体型と同じく , 型定義 で参照できるのです。添字は 0 から始まっ struct 構造体タグ名構造体型変数名 = { 体でしたが , 複数の同じ型の変数をまとめ て扱いたい場合があります。たとえば , 会 員名簿などは個人を表す Pers 。 n 型データを 人数ぶん集めたものとして表現できるでし よう。同じ型の変数の集合体を表現するの が配列型 ( a 「 ray type) です。配列型の概要 を Fig. 8 に示します。配列型に含まれる変 数は要素細 ement ) と呼ばれ , " 3 番目の要 素 " とか " 10 番目の要素 " という具合に添字 (index) で指定できます (Fig. & ② ) 。配列 名を a とすると , i 番目の要素は [ ] 演算子を 使って , a[ i ] ていることに注意してください。したがっ て , n 個の要素を持つ配列 a のいちばん最初 の要素は a [ 0 ] , いちばん最後の要素は a [ n - 1 ] ということになります。つまり , n 個の 要素を持っ配列に対する有効な添字の範囲 は 0 ~ n -1 です。気をつけなくてはならない のは , [ ] 演算子のなかに指定する添字に有 効でない整数値を書いても , 文法的には工 ラーではないということです。たとえば , int a [ 1 0 ] ; として宣言した配列に対して , a [ 13 ] という操作をすると , 配列が占めているメ モリ領域の外に値を書き込むことになり , Fig. 7 共用体型 double d; / * 実数データとして * / Cha 「 c; / * 文字データとして * / union CharO 「 Double { < 例 > 同じメモリ領域を文字または実数として扱う union 共用体タグ名 " の部分が型名となる く注意 > ・メンバは 1 個以上なくてはならない メンバ変数の型名 n メンバ変数名 n , メンバ変数の型名 1 メンバ変数名 1 , メンバ変数の型名 0 メンバ変数名 0 : く書式 > union 共用体タグ名 { ( 1 ) 共用体型の定義の仕方 ( 2 ) 共用体型変数の宣言の仕方 く書式 > く ) 主意 > く例 > union 共用体タグ名共用体型変数名 : union 共用体タグ名共用体型変数名 = { メンバ 0 の初期値 , union 共用体タグ名共用体型変数名 union CharOrDouble cod = { 'a' } ・初期化は最初のメンバに対してしか行えない = 同型の共用体変数または定数 : ( 3 ) 共用体メンバの参照 く書式 > 共用体型変数名 . メンバ名 く例 > cod. c = 'b' cod. d = 2.0 : / * cod を cha 「型変数として扱う * / / * cod を dou 団 e 型変数として扱う * /
を使いたいところで " PI " と書けば , それが 定数 " ( 3.14159265 ) " に展開されます。 このようにマクロ定義で定義された定数 をマクロ定数などと呼びます。マクロ名は マクロ名であること明示するために伝統的 に大文字で定義するようになっています。 C 言語にはいくつか定義済みのマクロ定 数があります。たとえば , _LINE__ や _FILE ーはそれぞれソースプログラム中で の行番号とファイル名にマクロ展開されま す。なお , このようにアンダーバーふたっ で始まるマクロ名は定義済みのマクロ定数 やコンパイラが内部で使用するマクロ名と して使えることになっているので , プログ ラマはそのようなマクロ名を定義してはい けません。また , マクロ定義するときは記 号列部分を省略して名前だけを定義するこ もよく , となってしまって , 意図した結果が得られ ADD ( 2 * x, 3 ) とができます。 ないことになります。 とした場合は , 引数付きマクロ定義 マクロ定義の解除 引数付きマクロ定義は , Fig. 31 のように と展開されます。 マクロは # undef によってその定義を解除 引数付きのマクロを定義できます。たとえ なお , ADD などの例で , マクロ定義され できます。たとえば , る記号列全体とそのなかの仮引数を ( ) でく #undef ADD #define ADD( a, b ) ((a) + (b)) くっているのは , 展開される式のなかで実 とすれば , マクロ ADD を解除することが可 と定義すれば , 引数が予期しない形で結合されるのを防止 能です。 ADD ( 1 , 2 ) するためです。たとえば , 条件付きテキスト取り込み #define ADD( a, b ) a + b というマクロは , ( ( 1 ) + ② ) と定義している場合にマクロ ADD で "(x 条件付きテキスト取り込み機能を使う * 2 ) に y を足して , さらにその合計を z 倍 " という記号列に展開されます。つまり , 引 と , ある条件が成立するときだけ , ソース 数付きマクロ定義によって一連の処理を関 プログラムの一部を有効にしたり無効にし しようとして , ADD ( x * 2 , y ) * z たりできます ( Fig. 32 ) 。条件式のなかでは , 数のように記述できるのです。この ADD は と記述したとき , これは , C 言語の関係演算子・等号演算子・不等号 足し算をするマクロというわけです。また , 各実引数は複数の記号から構成されていて 演算子・論理演算子などを使用することが x * 2 + y * Fig. 29 インクルードファイルの種類 ( 1 ) 標準ヘッダファイルのインクルード く書式 > #include く標準ヘッダファイル名 > ↑ く > でファイル名をくくる く例 > #include く stdio. h> ( 2 ) 標準外へッダファイルのインクルード く書式 > #include " 標準外へッダファイル名 " " でファイル名をくくる #include "mymodule. h Fig. 28 プリプロセッサと # include 命令 stdio. h #include<stdio. h> int main(viod) 読み込まれた stdio. h #include く stdio. h> int main(void) サ ッ ロ PP 加工 ソースファイル ソースファイル ば , Fig. 30 単純マクロ定義 く書式 > #define マクロ名記号列 #define マクロ名 #define 曰 ( 3.14159265 ) #define MY_NAME "Masano 「 i Ohshi 「 0 #define VERSION (0x0010 ) #define TEST く例 > ↑ ↑ く例 > 54 C MAGAZINE 2000 5
0 0 の C 言語クイックマスター は , 配列の先頭要素を間接参照します。そ して , 要素を参照する [ ] 演算子は配列に対 してだけでなく , 要素型へのポインタ型変数 [ 添字 ] として , ポインタ型変数にも使うことがで きるのです。 Fig. 13- ( 2 ) の例で見てみましよう。 doubl e 型配列 a と double * 型ポインタ叩が宣言さ Fig. 12 構造体への間接参照 ( 1 ) 構造体への間接参照 く書式 > * 構造体へのポインタ型変数名 : ( 2 ) 構造体メンバへの間接参照 く書式 > 構造へのポインタ型変数名 - > メンバ名 く注意 > ・・・ ( 構造体へのポインタ型変数名 ). メンバ名 " と等価 く例 > 複数素 cn の虚数を間接参照して 2 ℃を代入 double a[3] = { 4.0 , 7.5 , 1 .0 } で参照できることです。ポインタ型変数が のは , 同じ 2 番目の要素 a [ 1 ] を , 照しているのです。ここで注目してほしい は a [ 1 ] と同じ配列 a の 2 番目の要素を間接参 ap[l ] して , れており , 叩は a で初期化されています。そ メモリ アドレス 0 番地 x 番地 n 番地 struct CompIexNumbe 「 * cnp = &cn, st 「 uct CompIexNumbe 「 cn = { 3 ℃ 4.0 } : cnp->image = 2.0 : 0x0A 3.0 ( メン )ümage) 4.0 ( メンバ「 eal) Fig. 13 配列への間接参照 ( 1 ) 配列名とアドレスの関係 く注意 > = & 配列名 " はエラー。配列名は最初の要素 のアドレスを表す く例 > double a[31; double *ap = a; *a;*ap;/* ともに a 囘を間接参照する * / く注意 > = * ( 要素型へのポインタ変数名 + j ) " と等価 く書式 > 要素型へのポインタ変数名 [ j ] ( 2 ) 配列の j 番目の要素への間接参照 double *ap ー く例 > 右図の場合 ap[01; *ap; / * ともに a 囘を間接参照する * / メモリ アドレス x + 8 番地 x 番地 O 番地 x + 1 6 番地 n 番地 0x3E メモリ空間 0x0A 4 ℃ メモリ空間 0x3E ( 要素 a [ 21) ( 要素 a [ 1 ]) 7.5 ( 要素 a [ O]) 変数 cn このメンバを cnp->image で 参照できる ポインタ 変数 cnp ポインタ 変数 ap 配列 a 参照できる * ( ap + 1 ) で ap [ 1 ] または この要素を ap[ll; / * これは a 川を間接参照する * / / * これも a 川を間接参照する * / ※ double の大きさが 8 バイトの場合 メモリアドレスを数値として格納している 単なる整数値と考えると , ( 叩 + 1 ) は x + 1 番 地を表していることになります。しかし Fig. 13 の右図に示すように , 2 番目の要素 a [ 1 ] は x + 1 番地ではなく x + sizeof(double) 番地に あることになるので , これでは * ( 叩 + 1 ) で正しく要素 a [ 1 ] を参照できません。 これは , ポインタの計算に秘密があるの です。 T 型へのポインタ型変数 , T * tp; がある場合 , ポインタの計算 , tp の格納しているメモリアドレス + ( sizef( T ) * j ) というアドレス演算を行っているのです。 つまり , ポインタに対して整数 j を引いたり 足したりすることは , 元の要素の j 個前後の 要素を指し示すポインタ値を算出している わけです。そのため , ポインタ値 [ j ] * ( ポインタ値 + j ) は働きとしてはまったく同じことになりま す。実際 , オペランド 01 , 02 に対して演算 子 , 01 [ 02 ] は , は , * ( 01 + 02 ) と等価であると定義されているのです。 ところでこのように見てくると , 配列名 はそのままポインタ型変数として使えるよ うに思えますが , そうではありません。た とえば , int ia[ 5 ] ; int * iap = ia; という場合 , 配列名 ia に対して , la = lap; という具合にポインタ値を代入することは できません。その意味では , 配列名はポイ ンタ定数と考えることもできます。しかし , 配列名は単なるポインタ定数以上のもので す。たとえば , 配列名はその配列の大きさ を保持しています。 特集 2 C + + プログラマのための C 言語クイックマスター 45
0 0 の C 言語クイックマスター ( 初期化を含めた ) 変数宣言を , 型の整数として表現されています。なお , 構造体型と共用体型 struct ComplexNumber { Fig. 5 には示していませんが , 型定義と ( 初 期化を含めた ) 変数宣言を , double 「 e 酬 double image, enum Country { いくつかの情報をまとめてひとつの型と したい場合があります。たとえば , " 個人 " と一緒に行ってもかまいません。なお , 構 America, Japan, UK, USA = 0 造体のサイズは , 各メンバのサイズの合計 を表現するには , 名前 , 年齢 , 性別 , 住 } myCountry; よりも大きくなることがあることに注意し 所 , 電話番号などをまとめて Pers 。 n 型とい というように一緒に行ってもかまいません。 てください。これは , CPU が特定のメモリ う型にできたら便利です。 C 言語では , 複 ところで , Fig. 5- ( 1 ) の例では , " / * " と 位置に変数が配置されているほうが効率よ 数の変数や定数をまとめてひとつの新しい * / " の間に説明が入っていますが , " / * 型にしたものを構造体型 (structure type) と くアクセスできるなどの理由で , メンバの から " * / " まではプログラムには影響を与 間に使用されない領域を挿入して , 各メン 呼びます。構造体型の定義とその変数宣言 えないコメントとして扱われます。わかり バをもっとも効率のよい位置に配置するこ の仕方を Fig. 6 に示します。構造体に収め やすい注釈をコメントとして記述するよう とがあるからです。 にしましよう。なお , " / * " と " * / " を " 入 られたそれぞれの変数はメンバ (member) ときに , 同じ変数のために割り当てられ と呼ばれ , Fig. 6- ( 3 ) のように . 演算子を使 れ子 " にして記述することはできません。つ たメモリ領域を複数の別の目的で使い分け まり , って , たい場合があります。たとえばある変数を , 構造体変数名 . メンバ名 あるときは文字データを格納する変数とし とすることで取り扱うことができます。ま というようには使用できないので注意して て , あるときは実数データを格納する変数 た , Fig. 6 には示していませんが型定義と ください。 Fig. 5 列挙型を使った整数定数 ( 1 リ挙型の定義の仕方 く書式 > enum 列挙タグ名 { 定数名 O = 値 O, 定数名 1 = 値 1 , 定数名 n = 値 n く注意 > = = 値 " は省略可能。その場合は , 定数値は直前の定数値に 1 加え た値となる。いちばん最初の定数で値を省略すると , 0 が指 定されたものとみなされる ・定義する定数は 1 個以上なくてはならない ・ enum 列挙タグ名 " の部分が型名となる く例 > 国別コード enum Country { Ame 「 ica, Japan, USA = 0 Fig. 4 const 定数の定義の仕方 く書式 > const 定数の型名定数名 = 定数値 : く例 > const int i = 0; const cha 「 cl const double d = 0.1 : TabIe 1 数値計算をする演算子の例 種類 四則演算子 十 % ( オペランドは整数 ) 剰余演算子 & , に ^ , ~ ( オペランドは整数 ) ビット演算子 > > , くく ( オペランドは整数 ) シフト演算子 インクリメント演算子 十十 デクリメント演算子 複合代入演算子 演算子 / * 定数値は O となる * / / * 定数値は 1 となる * / / * 定数値は 2 となる * / / * 定数値は 0 となる * / くく = ( 2 ) 列挙型変数の宣 く書式 > enum 列挙タグ名列挙型変数名 , enum 列挙タグ名列挙型変数名 = 定数値 , く注意 > ・列挙型の定数および変数は int 型で表現されている く例 > 前例の enum Count Ⅳ型を使う例 enum Country myCountry = Japan; myCountry = -1 : / * このように列挙型以外の整数値も代入できてしまう ことに注意 * / TabIe 2 A ビット演算 A と B の排他的論理和 A の否定 A と B の積 A と B の和 0 1 1 1 0 0 0 B 0 0 1 1 ・ 0 1 ー 0 1 1 1 0 0 1 0 1 0 1 41 特集 2 C + + プログラマのための C 言語クイックマスター
0 0 の C 言語クイックマスター できます。また , 条件式のなかでは , defined( マクロ名 ) としてマクロ名が定義されているかどうか #if ! defined( マクロ名 ) を検査できます。これは , マクロ名が定義 の短縮形としてそれぞれ , されている場合は 1 を , 未定義なら 0 を返し #ifdef マクロ名 ます。また , #indef マクロ名 #ifdefined( マクロ名 ) という形式を使用することも可能です。 条件付きテキスト取り込みによって , #if 0 コメントにしたい部分 #endif とすることで , ソースプログラムの広範囲 の部分をコメントにできます。また , Fig. 32 の例 1 のようにデバッグ用のコードを一 時的に入れることもできるし , Fig. 32 の例 2 のように ANSI 規格に沿った書き方とそうで ない書き方を並記することもできます ( ーー S TDC__ は ANSI 規格に沿った C コンパイラで 定義されているマクロです ) 。同様に , オ ペレーティングシステムに依存する部分を 書き分けることにも利用できます。 モジュールとヘッダファイル さて , ⅱ stl では stdio. h というファイルを インクルードしていましたが , これは画面 への文字の表示やファイルへのデータの入 出力を行う関数群や型などを使用するため に必要なファイルです。 ANSI 規格に準拠し た C 言語コンパイラには , 規格で定められ ている標準の関数が数多く用意されていて , それらを利用することができるようになっ ています。この関数群を標準ライブラリ (standard library) と呼びます。 標準ライプラリの関数はいくつかの互い に関連するもの同士が集められてグループ としてまとめられています。この関連し合 う関数の集まりをモジュール ( modu と呼 びます ( このモジュールもソースプログラム をコンパイルすることで得られます ) 。 そして , 各モジュールにはそのモジュー ルを使用するのに必要な情報を記述したフ ァイルが用意されています。これを一般に ヘッダファイル ( header file) と呼びます (Fig. 33 ) 。 C 言語のヘッダファイル名は伝 統的に拡張子 ". h " を付けるようになってい List1 でインクルードしている stdio. h は標 準のデータ入出力用モジュールのヘッダフ ァイルなのです ( stdio は STanDard lnput 特集 2 C + + プログラマのための C 言語クイックマスター 55 Fig. 31 引数付きマクロ定義 く書式 > #define マクロ名 ( 仮引数 1 , ・・・ , 仮引数 n ) 記号列 ↑ ↑ マクロ名と ( を離しては この記号列は引数を 使って記述できる いけない く例 > #define AVERAGE(a,b) (((a) + (b))/2) #define SWAP_NUMBERS(a,b) { long double c;c = a;a = b;b = c; } Fig. 32 条件付きテキスト取り込み く書式 > #if 条件式 A #if 条件式 A ・範囲 1 範囲 1 #endif 範囲 2 #if 条件式 A 範囲 1 #elif 条件式 B 範囲 3 #endif #else #endif ・条件式は long 型または unsigned long 型の整数式 ・条件式 A が真のとき範囲 1 が有効になり , 偽のとき範囲 2 が有効になる ・条件式 A が偽かっ条件式 B が真のときだけ , 範囲 3 が有効になる ・条件式のなかで defined ( 名前 ) を用いて , 特定の名前がマクロとして定義されているか どうかを調べられる #define DEBUG 1 #if DEBUG / * デバッグ用のコード * / #endif #if definedC—STDC__) / * ANSI C 用のコード * / #else / * 非 AN C 用のコード * / #endif く例 1 > く例 2 > Fig. 33 モジュールとヘッダファイル ヘッダファイル モジュール ( 機械語にコンバイル済み ) 変数 関数 関数
0 0 の C 言語クイックマスター このように , 翻訳単位の外部から参照で きることを , それらの名前は " 外部リンケ ージ (externallinkage) を持つ " といいます。 実は , 外部リンケージを明示するには exter n という予約語を使って , extern 返却値型関数名 ( 仮引数リスト ) { ・・ extern 型名大域変数名 = 初期化値 ; と宣言する方法もあるのですが , extern は 省略可能なので extern を付けなければ自動 的に外部リンケージを持っことになるので す。 しかし逆に , これら大域の関数名・変数 名・ const 定数名を , 翻訳単位の外部から 参照できないように隠しておきたいときも あります。その場合 , 予約語 s ねⅱ c を明示的 に付けて公開したくない名前を宣言する必 要があります ( たとえば Fig. 35 の " 参照され るモジュール A " の関数 f ( ) と大域変数 a ) 。 こまでは参照される側の外部リ さて , ンケージについての話でした。一方 , 参照 する側は , ソースファイルで定義・旦言さ れていない外部の関数名や変数名を使うた めに , 外部参照する対象の型を明確にコン パイラに伝えておかなければいけません。 そのための方法が外部参照宣言です。外部 参照宣言は , 返却値型関数名 ( 仮引数リスト ) ; extern 型名大域変数名 ; という形になります。前者は , 関数の型を 完全に規定する宣言で , 関数プロトタイプ (function prototype) 宣言と呼ばれます。気 をつけなければならないのは , 後者の大域 変数の外部参照宣言です。もし大域変数を 日言するときに初期化を忘れると , 外部参 照宣言とまったく同じ形になってしまいま す。そうなると , どこに実体が宣言されて いるのかが曖味になってしまうのです。で すから , 大域変数はどこか 1 か所で必ず初 期化を伴った宣言をして実体を与えておか なければなりません。一般に初期化を伴っ た大域変数の宣言を , 外部参照宣言と明確 に区別するために " 大域変数の定義 " と呼び ます。 では最後に外部参照が解決されるまでの 行程を見てみましよう。まず , コンパイラ はコンパイル中に宣言されていない変数名 と定義されていない関数名を見つけると , Fig. 35 外部定義された大域変数・関数の公開および非公開処置と参照 参照されるモジュー丿レ A static 付き定義 ( 非公開 ) 関数 static 型 f ( ) {... } 変数 static 型 a = 初期値 : 非公開にしたいものは static 付きで定義 static なし定義 ( 公開 ) 型 b = 初期値 , 関数 変数 リンク情報 リンカ INKE モジュール B は A の g() と b を使っている な。参照と実体を糸口 合しよう それを外部参照の対象とみなします。その とき , 外部参照宣言があればそれを元に参 照している部分をコンパイルし , 実体との 結合はリンカに任せます ( たとえば Fig. 35 の " 参照側モジュール B " ) 。もし外部参照宣 言がなければ , 適当に型を推測してコンパ イルしてしまいます。これが実際の型と一 致していない場合は , リンク作業のエラー やプログラムの動作不良の原因になります。 そして , リンカは各モジュールの持つリン ク情報を調べ , 参照側と参照される実体を 結び付けるというわけです。 ヘッダファイルとモジュールの作成 さて , ヘッダファイルには何を書けばい いのでしよう。まず , モジュールで定義さ れている型・列挙定数・マクロ定義の一部 は , モジュールを利用する側でも必要にな るでしようから , それらを入れる必要があ ります。また , モジュールを利用する側が いちいちすべての外部参照宣言を自前で記 述するのはたいへんです。そこで , モジュ ールのヘッダファイルに , そのモジュール が外部に公開する大域変数と関数の外部参 力にまかせよう ているな。結合はリン れている g ( ) と b を使っ 他モジュールで定義さ exte 「 n 型 b; 型 g(); 外部参照宣言 参照側モジュール B 公開にしたいものは static なしで定義 外部参照したい関数のプロトタイプ宣 言と大域変数の外部参照宣言を行う 特集 2 C + + プログラマのための C 言語クイックマスター 57
ちます。これをファイルスコープ ( f ⅱ e scope) Output の略 ) 。 List 1 では main 関数のなかで その関数のなかだけで有効であり , それら ⅵ n ば ( ) という関数を呼び出しています。 と呼びます。 のスコープは " 関数ローカル " であるといえ これら大域の識別子のうち , " 実体 " を持 の関数は標準入出力ライプラリの一員で , ます。 そのために stdio. h をインクルードする必要 一方 , 関数の外で定義した型名・列挙定 っている名前 , すなわち関数と変数名 , const 定数名はほかのソースファイル ( 厳密 があるわけです。 数名・変数名・ const 定数名そして関数名 には翻訳単位 ) から参照することが可能で List 1 を実行すると , main() 関数が printf 自体などは , 大域 ( グローバル , global) ス ( ) 関数を呼び出し , p ⅱ n ば ( ) 関数が実引数 す。これは , コンパイルされる際に , リン コープを持つ , といいます。コンパイル時 に与えられた文字列 " He Ⅱ 0 WorId!Yn" を画 には , これらの識別子は宣言された場所か 力が外部からこれらの名前を参照できるた 面に出力します。末尾の " \ n " は出力されて らその翻訳単位の末尾までのスコープを持 めの情報も一緒に生成されるからです。 いないように見えますが , これは改行を表 Fig. 34 C 言語のシステム構成 した特別な文字指定です。ですから , 画面 上では " He Ⅱ 0 WorId ! " という文字列の後ろ で改行されているはずです。 p ⅱ n ば ( ) 関数 は , データを文字列に変換し , いろいろな 書式 ( 左詰めだとか何桁表示とか ) を指定し て出力できる便利な関数です。 C 言語のシステム Fig. 34 に C 言語のプログラムがどのよう に処理されて実行可能なプログラムになる か , そのシステムの全体像を示します。 まず , ソースプログ - ラムがプリプロセッ サによって処理されます ( インクルードフ ァイルの読み込み , マクロ展開などが行わ れる ) 。そして , コンパイラによって機械 語に翻訳され , モジュールになります。 Fig. 34 に示したソースファイルは main 関数 を含んでいるので , 実行可能なプログラム にすることができます。リンカ ( ⅱ nke 「 ) とい うプログラムが , 各モジュールで参照し合 っている変数や関数を調べ , 必要なモジュ ールを結合します。そして , 実行可能なプ ログラムができあがるというわけです。 では , ヘッダファイルには何が記述され ているのでしよう ? また , 自作のモジュ ールとヘッダファイルを作るにはどのよう にしたらよいのでしよう ? これらの疑問 に答えるためには , モジュール間の参照を どのようにリンカが解決しているかを知ら なければなりません。 外部参照 前述したように , 関数の内部で宣言・定 義された型名・変数名・ const 定数名は , 56 C MAGAZINE 2000 5 ソースファイル maln ヘッダファイル 標準ライブラリ関数 stdio. h ④〇。◎ ④〇 math. h 標準入出力 モジュール プリ プロセッサ 气 PP 関数数学演算関数 モジュール 加工 加工済み ソース プログラム ( 翻訳単位 ) ユーサの mymodule.h 自作モジュ 関数 OM コンバイラ リンカ INKE コンパイル リンク 関数 関数 rnaln main 関数を含んだ モジュール 関数 maln 関数 実行可能なプログラム
char c = 0xAA char * cp = &c; としたとき , * cp は c の代わりに使うこと ができ , * cp = 0X01 ; で , c に値脈 01 を代入できます。このよう ムの誤動作の原因になります。ですからポ 参照した場所に値を書き込むと , プログラ アドレス値を持つポインタ型変数から間接 いるかわからないので , そのような不定な 不定なアドレス値はメモリのどこを指して 定な値が入っていることになります。この に宣言すると , そのポインタ型変数には不 なお , ポインタ型変数を初期化を伴わず ます。 上の変数を参照することを間接参照と呼び にポインタ型変数から間接的にメモリ空間 指し示す型の型名 * インタ変数は , から するときに適当なアドレスで初期化できな 味です ) 。ですから , ポインタ変数を宣言 に使われます ( void は " 無 " とか " 空 " という意 が「何も指し示していない」ことを表すため 型です。この空ポインタは , そのポインタ で , その型は void * , つまり汎用ポインタ 整数定数 0 または定数 NULL 空ポインタは , ポインタ ( n ⅶ pointer ) が用意されています。 11- ( 1 ) ) 。また , 特別なポインタ値として空 型の変数に代入することが可能です ( Fig. にどんなポインタ型の値でも汎用ポインタ は , どんな型のポインタでも代入でき , 逆 があります。この汎用ポインタ型の変数に VOid * 型と呼ばれる特別な型 , C 言語のポインタ型には , 汎用ポインタ 汎用ポインタと空ポインタ よいでしよう。 というように , 初期化を伴って宣言すると ポインタ型変数名 = & 変数名 ; 44 C MAGAZINE 2000 5 と空ポインタで初期化しておき , そのポイ int * ip = NULL; い場合は Fig. 10 メモリアドレスとポインタ型 ( 1 ) メモリ空間の概念図 メモリ アドレス 0 番地 1 番地 x 番地 y 番地 y + 1 番地 n 番地 0xFF 0x2B 0xAA 0X11 0X11 0xE8 ポインタ 変数 cp char 型 変数 c short 型 変数 s ポインタ 変数 sp メモリ空間 ※ 1 バイトことにメモリアドレスが割り振ら れていて , cha 「型変数と sho 「 t 型変数の大 きさがそれぞれ 1 バイト , 2 バイトである システムの例 Fig. 11 汎用ポインタ型 void * と空ポインタ ( 1 ) 汎用ポインタ型 void * く注意 > ・ void * 型のポインタに対しては , ど く例 > んな型のポインタでも初期化およ び代入することができる ・どんな型のポインタに対しても , void * 型のポインタで初期化およ び代入することができる int i = 0 : VOid *a = &i; int *ip = a; ( 2 ) 空ポインタ く書式 > 整数定数 0 または定数 NULL く注意 > ・空ポインタは void * 型の特別な定 数で , 何もポイントしてないポイン タ値を意味する ・空ポインタは必すしもメモリアドレ ス 0 を意味しているわけではない ・空ポインタは数値として扱われる 場合は整数定数 O となる ンタがまだ何も示していないということを 明示させるべきです。こうすれば , ポイン タを扱うところで , その値が空ポインタか どうかをチェックすることで , 値が未設定 ( 2 ) ポインタ型変数の宣 < 書式 > 指し示す型の型名 * ポインタ型変数名 : 指し示す型の型名 * ポインタ型変数名 : = 同型もしくは void * 型のポインタ値 : く注意 > " 指し示す型の型名 * " の部分が型名となる ( 3 ) 変数のメモリアドレス値を取り出す く書式 > & 変数名 く例 > 左図のポインタ変数 cp と sp char c = 0xAA ・ sho 「 t s = 0X1111 : char * cp = &c; short * sp = &s; ( 4 ) ポイントしている先のメモリ領域を変数として参照 する ( 間接参照 ) < 書式 > * ポインタ型変数名 く例 > 左図の変数 c に 0x01 を書き込む cha 「 c = 0xAA char * cp = &c, * cp = OXOI, のポインタから間接参照するミスを防ぐこ とが可能です。 構造体と配列への間接参照 さて , 構造体と配列への間接参照の方法 を説明します。構造体変数全体を間接参照 するぶんには , 通常の変数を間接参照する 場合と変わりません ( Fig. 12- ( 1 ) ) 。しかし , 構造体のメンバを参照するときは , ( * 構造体へのポインタ型変数名 ). の代わりに演算子 - > を用いて , 構造体へのポインタ型変数名 - > メンバ名 という短縮形を使用することが可能です (Fig. 12- ② ) 。 配列への間接参照は , C 言語の仕組みの なかでもおもしろいもののひとつです。実 は , 配列名はその先頭要素へのポインタ値 を表しているのです ( Fig. 13- ( 1 ) ) 。ですか * 配列名 メンノヾ名
定期購読雑誌ー 発売日 雑誌名 UNIX USER Notes/Domino magazine DOS/Vmagazine ① 15 日・ 29 日 DOS/Vmagazine ② 15 日・ 29 日 C MAGAZINE INTEROP MAGAZINE 24 日 18 日 13 日 8 日 ・お申し込み締め切り日は各雑誌とも、発売日の 1 0 営業日前になります。 期間 ( 回数 ) 年間 ( 12 回 ) 年間 ( 12 回 ) 年間 ( 24 回 ) 半年間 ( 12 回 ) 年間 ( 12 回 ) 年間 ( 12 回 ) 料金 ¥ 15 , 600 ¥ 17 , 160 ¥ 21 , 360 ¥ 10 , 680 ¥ 14 , 400 ¥ 15 , 360 ・発送開始号は、お申し込み締め切り日によって設定させていただいております。 ・すでに発売されている号、間に合わなかった号は、お近くの書店にてお求めください。 ・定期購読開始後、お支払用紙記載のお支払期限までにお支払いいただけなかった場合は、購読を停止させていただくことがござい ますので、 ご了承ください。 会員番号 お名前 フリガナ 性別 登録住所 都道府県名 住所 ビル名称 電話番号 携帯電話 番号 E-mail 契約 年 個人・法人 印鑑 生年月日 1 9 自宅・勤務先・その他郵便番号 名 名 男 女 勤務先の場合 会社 / 学校名 部署 / 学部名 役職名 雑誌名 Fax 番号 内線番号 部数 月 業 職 日 種 種 の部分は必須項目です。 土日祝日はお休み 営業時間 : 09 : 30 ~ 12 ℃ 0 13 ℃ 0 ~ 17 : 45 TEL : 03-5642-8481 /FAX : 03-5642-7272 〒 103-8508 東京都中央区日本橋箱崎町 24-1 定期購読センター ソフトバンクバブリッシング株式会社 お問合せ先 15. その他 弁護士等 ) 10. 経営者・役員 11 . 自由業 12. 無職 13. 学生 14. 主婦 5. 事務職 6. 営業・販売職 7. 企画色 8. 教師・講師 9. 専門職 ( 医師・ 1 . 技術職 2. 研究職 3. コンピュータ関連技術職 4. プログラマ・ SE あなたの職種 16. 出版・マスコミ・広告 17. その他 13. 官公庁・公共機関 14. 学校・教育関係 15. 弁護士・会計事務所 9. 建設・土木 10. 金融・保険・不動産 11 . 運輸・通信 12. 医療機関 ョップ 6. サービス業 7 情報処理・ソフトハウス 8. コンサルタント ータ関連 4. ( 3 以外の ) 商社及び卸・小売り 5. コンピュータ関連シ 1 . 製造業 ( コンピュータ関連 ) 2. 製造業 ( 1 以外 ) 3. 商社 ( コンピュ 動務先の業種 番号をご記入ください。 勤務先の業種・職種は以下の選択肢からお選びになり、 お名前をご記入ください 会社でのお申し込みの場合にも、ご担当者様として必ず 入ください。 お名前以下は、ご登録情報に変更がある場合のみ、ご記 すでに会員の方は、会員番号をご記入ください。その際、