定義 - みる会図書館


検索対象: 月刊 C MAGAZINE 1993年12月号
67件見つかりました。

1. 月刊 C MAGAZINE 1993年12月号

①調調實 y ①、 in ① 壊関数の仕事は , したがって , そのデータ モジュールがインクルードしていたら , ハ List 1 と List 2 は , Set という抽象データ 構造のメモリをフリーにすることて、す。 ンドルのタイプは void を指すポインタとし タイプのスケルトンて、す。 Set は (Pascal の 実装モジュールは , ハンドルのタイプを て定義します。一方 , 自分の実装モジュー SET のように ) 離散値の非順序集合て、す。 S ルの中にインクルードされていたら , ハン 構造体 , 配列などその実装本体を指す , ポイ et に対する重要な操作は , 「 X は Set Y のメ ンタとして定義しなければなりません。さ ドルのタイプの定義を変えてはいけません。 ンバか ? 」とか「 SetA を SetB と SetC の和 らに , そのタイプ定義は , 自分用の定義モ そこて、定義モジュールの中には , Fig. 1 のよ とせよ」などの操作て、す。最大て、サイズが ジュールをインクルードする # include 文より うな文を書きます。 MAX CARDINALITY( これは List 1 の中 も前に書き , プロトタイプが処理されると これは , 各サーバモジュールが何らかの て、は 65 , 500 ) まて、の Set を作れます。 シンポルを定義して , それを定義モジュー きにはインタフェイスの中て、使われるタイ 本稿用にはインタフェイスはうんと小さ プ名が定義済みて、あるように , しなければ ルと共用するようにすれば簡単に実現て、き くしてあります。もちろん , Set のような抽 ます〔 List 2 の中のシンボ、ル IMP SET なりません。 象データタイプに対してはもっといろいろ ただし , 定義モジュールをクライアント な操作を定義しなければなりません。しか モジュールや自分の実装モジュールがイン 定義モジュールは , そのシンボ、ルが定義 し現状て、は , List 1 のインタフェイスにある クルードしているかどうかという判断を , 済みかチェックし , 定義されていなければ 関数の最後の三つは , 実装がありません。 コンパイル時に定義モジュール自身にやら ハンドルのタイプをクライアントモジュー IMP SETS というシンポルを実装モジュ せるという方法もあります。クライアント ル向けに定義変えします。 ールが定義しています。このシンボ、ルが定 義されていないと , 定義モジュールは自分 抽象テータタイプ「 Set 」の使用例 がクライアントモジュールの中にインクル ードされていると判断します。そのときは , 定義モジュールはハンドルのタイプ HSET を void を指すポインタとして定義します。 List 3 はクライアントの一例て、 , Set の生 成 , 利用 , 破壊 , ディスクへのストアと読 み出しをそれぞれ示しています。 ータへのアクセス 注意していただきたいのは , データへの アクセスは必す関数コールを介するという 点て、す。構造体のたったひとつのフィール ドの値を知るためだけて、も , 関数コールと いう受け入れがたいオーバヘッドがかかり ます。こういうとき , 二つの対策が思い浮 かびます。ひとつは , 短い関数をインライ ン関数に展開て、きるコンパイラを使うこと てす ( 厳しい警察官のような C 十十て、は許さ れませんが , C て、は平気て、す ) 。あるいは開 発の最後にアクセス関数を定義モジュール に移し , それらをマクロとして実装するこ とて、す。 上の第二の方法だと , 開発時には抽象デ ータタイプの安全性を利用て、き , そして安 定的なコードが完成したらマクロの効率を List 1 : #include ” Sets. h ” 2 : # i ncl ude く s td i 0. h 〉 3 : #include く malloc. h> 4 : 5 : #define SETSIZE 6 : 7 : vo i d ma i n ( vo i d ) HSET 9 : hs; unsigned short Size; FILE *hf; *ptr; 13 : hs ニ SetCreate (SETSIZE); 〃 Se t を作る SetInclude (hs, 55 ) : 1 5 : 〃 Se t に適当な項目を入れる SetInclude (hs, 15 ) ; SetExclude (hs, 12 ) , 17 : 〃 Set から項目を削除する SetExclude (hs. 15 ) : SetIncIude (hs, 14 ) : if(SetIsInSet (hs, 55 ) ) / / メンハ・一シッフ。をテストする 20 : printf ( ” 55 is a member Of the setYn"); 22 : printf ( " 55 is not a member Of the set\n"); 23 : 24 : 2 5 : / * ここでは Se t をディスクファイルへ書き込む * / 26 : hf ニ fopen ("SetImage. DAT" 27 : = SetQuerYImgSize (hs), 28 : S i ze ptr ニ malloc (Size); 29 : SetWriteDiskImg (hs, ptr); 30 : fwrite (&Size, sizeof(unsigned short), 31 : fwrite (ptr, Size, 1 , hf); 32 : fclose (hf) : 33 : free (ptr); 34 . SetDestroy (hs) ; 35 : 36 : 37 : / * ここでは s e t をディスクファイルから読み出す * / 38 : hf : fopen ("SetImage. DAT' 39 : fread (&Size, sizeof(unsigned short), 40 : ptr ニ malloc (Size); fread (ptr, Size, l,hf); 42 : ニ SetCreateFromDiskImg (ptr) : 43 : free (ptr); 44 : e ー se 〃 Set のサイガをストア / / Set のイメーデをストア 28 C MAGAZINE 1993 12

2. 月刊 C MAGAZINE 1993年12月号

抽象データタイプ「 Set 」のスケルトン ( 実装 ) List ion, あるアクセスが行なわれているときに は別のアクセスに待ったをかけること ) を提 供するのて、す。 この方法は再利用性に富む抽象 データタイプの開発を促進します。コード の再利用という目標は , 一定のライプラリ を通じてしかデータにアクセスて、きないと いう状態のときにいちばん達成しやすいの て、す。なぜなら , インタフェイスに欠けて いる機能を〔関数の一覧表などを見て〕素 早く識別て、きるからて、す。 ModuIa -2 の中心的な考え方は , 定義モジ ュール (definition modules) と実装モジュー ル (implementation modules) の分離て、す ( 本稿て、は、、モジュール〃とは一個のソースフ ァイルのことを指します ) 。定義モジュール は , C て、はモジュールに対するインタフェイ スを提供するインクルードファイル〔 . h フ ァイル〕に相当します〔したがってここて、 の、、定義クは , C 語て、言えばむしろ、、宣言クて、あ り , 、、実装〃が C 語て、言えば、、定義クに相当す る〕。 定義モジュールには , その抽象データタ イプが提供するすべての操作 ( 関数 ) のプロ トタイプを収めます。また , そのモジュー ルに関係のあるタイプ〔データ型〕も定義 します。抽象データタイプにとって最も重 要なタイプは , そのモジュールの存在理由 , すなわちその抽象データタイプのハンドル のタイプ名て、す。 実装モジュールは , C のソースファイルに 相当します。このモジュールには , その抽 象データタイプの実装に用いるデータ構造 のタイプ定義が入屮ます。また , 定義モジ ュールに記述〔 ~ 言〕したアクセス関数 のそれぞれの手続き〔 = C 言語て、言えば定義〕 , こに書きます。 実装モジュールは , 自分の定義モジュー ルをインクルードして , 自分の公開インタ フェイスの仕様を明確化します。実装モジ ュールには , 公開インタフェイス以外の手 続きも必要に応じて含めることがて、きます。 そういう内部的な関数は , プロトタイプを 1 : 2 : Sets. c 3 : Set の実装モジュール 4 : 5 : 説明 : 汎用の抽象データタイプ Set の実装を提供する . 6 : 7 : 8 : ーライプラリのインクルード 1 1 : #include 1 5 : #define IMP_SETS 1 6 : #define WORDSIZE 17 : #define BYTESPWORD 18 : #define FALSE 1 9 : #define TRUE 20 : 22 : 23 : typedef int BITWORD, 24 : struct SetRec { 25 : unsigned short NumElements, NumWords; * BSe t ; 〃 BSet は BITWORD の配列 26 . BITWORD 27 : 28 . typedef struct SetRec SETHEADER; / / HSET の実装を定義 29 : typedef SETHEADER * HSET ” Sets. h ” / / 自分自身のインタフェイスをインクルードする 30 : #include ローカルプロトタイプー 32 : / * 34 : BITWORD ValToBit (unsigned short Val); 35 : 37 : 38 : HSET 40 : HSET hs, hs ニ (HSET)malloc(sizeof(SETHEADER)) ; S i ze ; 42 : hs- 〉 NumElements ((Size-l)/WORDSIZE) + 1 : 43 : hs->NumWords (BITWORD *)malloc(hs->NumWords * sizeof(BITWORD)) ; 44 : hs- 〉 BSet SetClear (hs); 45 . 46 : return hs; 48 : unsigned short 49 : free (hs->BSet) : 50 : free (hs) ; return SET_SUCCESS; 5 2 : 5 3 : } 54 : unsigned short unsigned short 56 : if (hs) { 57 : for ( i ニ 0 ; i く hs- 〉 NumWords; i + + ) 58 : 59 : hs- 〉 BSetCi) 60 : return SET_SUCCESS; } else 62 : return SETERR_BADHANDLE; 6 3 : } 64 : BITWORD ValToBi t (unsigned short Val) 66 : BITWORD bset; 67 : bset ニ ( 1 くく Val); 68 : return bset; 70 : unsigned short 73 : 74 . 75 : く malloc. h 〉 ーローカルシンポルの定義 / / 1 ワードのビット数 〃 1 ワードのパイト数 11 ワ 3 0 ー 1 ーローカルタイプー - 実コード SetCreate (unsigned short Size) SetDestroy (HSET hs) SetClear (HSET hs) SetInclude (HSET hs, unsigned short VaI) unsigned short i dx, Mask , idx ニ Val/WORDSIZE; Mask ニ ValToBit (VaI % W()RDSIZE); if (idx く hs->NumWords) hs- 〉 BSetCidx] Mask; 26 C MAGAZINE 1993 12

3. 月刊 C MAGAZINE 1993年12月号

more 未指定 (unspecified) 未定義 (undefined) たとえば , ここて、あげられているほかに も , 実行時にどのような文字セットを使用 するかとか , int のビット数が何ビットて、あ るかとか , double の表現範囲は 10 の何乗ま て、かといったような問題はすべて処理系定 義てある。 一方 , 最適化の具合によって , ケースパ イケースて、コンパイルのされ方が変化する ような項目 ( たとえば上にあげられている引 数の評価順序 ) 以外にも , 部分式の評価順序 や式内部て、の副作用の発生タイミングなど , あるいは , 文字出力て、 , 画面の先頭に出力 位置がいる状態て、ノヾックスペースの文字コ ードを出力すると何が起きるかとか , 静的 オプジェクトがいっ初期化されるかといっ た , コンパイラ以外の環境 ( 端末表示系やリ ンカなど ) にも強く依存するだろう項目や , さらには ( 意外かもしれないが ) , 浮動小数 型の内部表現の詳細といった項目は「未指 定」となっている。こうして見てみると , 未 指定と処理系定義というのは微妙な関係に あることがわかる。要は , いずれも処理系 や環境に依存して定められるものて、あって , そのうちて、 , 処理系製作者がドキュメント に明記することが要求されているものは処 理系定義て、あり , とくに明記する必要がな いもの ( したがって , 首尾一貫していなくて もかまわないもの ) が未指定となっていると 考えられる。 格準拠のプログラムとは さて , unspecified, undefined, implem entation-defined の説明が終わったのて、 , 次 に , 規格に準拠したプログラムとは何かと いう問題を考察してみよう (Fig. 4 ) 。 最初に一点補足をしておこう。最初のパ ラグラフの末尾の文の中の , 「処理系の制限 値に関する最低値の規定を越えてはならな い」は , 一見意味不明かもしれない。「最低 値を越えてはならない」というのは変に思え Fig. 2 三つの用語の定義① ( 1.6 DEFINITIONS OF TERMS ( 用語の定義 ) より ) ANSI C ー more 131 新しい右隣ピットの値へと伝搬する。これは定義された動作のはすである。 論理右シフトを行うならば , 新しい値は常に 0 である。しかし , 古い最上位ビットの値は , 確実に なるのかが問われているのである。算術右シフトを行うならば , 新しい値は古い値と同じになる。 注 : p 「 opagation は「伝搬」だが , ここでは実際にシフトした結果 , 新しい最上位ビットの値が何に ように処理されるかがあげられる。 「処理系定義動作」の例としては , 符号っき整数が右シフトされたときに , 最上位ピットがどの bit when a signed integer is shifted right. An example Of implementation-defined behavior is the propagation Of the high-order 「未定義動作」の例としては , 整数のオーパフロー時の挙動があげられる。 An example Of undefined behavior is the behavior on integer ove 用 OW. 「未指定動作」の例としては , 関数へ渡される実引数が評価される順序があげられる。 are evaluated. An example Of unspecified behavior is the order in which the argumentsto a function Fig. 3 三つの用語の定義② ( ANS 凵 .6 DEFINITIONS OF TERMS より ) おり , そして各処理系がドキュメントしていなければならないような動作をいう。 正しいプログラムの構成と , 正しいテータに関する動作で , 処理系の実装上の性質に依存して •lmplementation-defined behavior ( 処理系定義動作 ) しない。それらはすべて同一の「未定義である動作」を記述している。 する明示的な定義を省路していることで示される。しかし , これら 3 種類の表現の間に優劣は存在 ろでの未定義動作は , 「 undefined behavio 「」という用語で明示されているか , あるいは動作に関 といった要求に違反した場合には , その動作は未定義である。本標準において , それ以外のとこ 制約条件 (constraint) 以外の部分に記されている「 ~ ねばならない」とか「 ~ であってはならない」 実行を ( 診断メッセージを表示して ) 停止するといったところまでがあり得る。 ントされた方法で反応する ( 診断メッセージを出力してもしなくてもよい ) とか , あるいは翻訳や ものから始まって , プログラムの翻訳中や実効中に , 環境の性質に応じて , あらかじめドキュメ 作として許容できる範囲は , その状況を完全に無視してしまって , 予測不能な結果を得るという できないオプジェクトの使用に関する動作で , 標準が何も要求を行わないものをいう。未定義動 移植不能 , もしくは誤りのあるプログラムの構成 , あるいは誤ったテータ , あるいは値が決定 ・ Undefined behavio 「 ( 未定義動作 ) いう。 正しいプログラムの構成と , 正しいテータに関する動作で , 標準が何も要求を行わないものを ・ Unspecified behavio 「 ( 未指定動作 ) each implementation shall document. and correct data, that depends on the characteristics Of the implementation and that * lmplementation-defined behavior ー behavior, fO 「 a correct prog 「 am construct behavior that is undefined. behavior. There is no difference in emphasis among these three : they all describe by the words "undefined behavior 0 「 by the omission Of any explicit definition Of the behavior is undefined. Undefined behavior is otherwise indicated in this Standard げ a shall" 0 「 shall not" requirement that appears outside Of a constraint is violated (with the issuance Of a diagnostic message). without the issuance Of a diagnostic message) , tO te 「 minating a translation 0 「 execution program execution in a documented manner characteristic Of the environment (with 0 「 the situation completely with unpredictable results, tO behaving during translation 0 「 Standard imposes no requirements. Permissible undefined behavior 「 anges from ignoring 「 am construct, Of erroneous data, 0 「 Of indeterminately-valued objects, fO 「 which the behavior, upon use Of a nonportable or erroneous prog * Undefined behavior - data, fO 「 which the Standard imposes no requirements. behavior, fO 「 a correct program construct and C0「「 ect * Unspecified behavior ー

4. 月刊 C MAGAZINE 1993年12月号

の能力の一部てす。 〔ないし関数〕の柔軟性と再利用性は大いに タイプはそれぞれ , そのデータに対して期 この比喩はもちろん , インタフェイスを 高まります。つまり , 抽象データタイプは , 待される各種の操作以外に生成関数と破壊 正しく理解することの重要性を強調してい 自分のインスタンスがいくっ作られるのか 関数をそなえなければなりません。 ます。あなたとあなたの犬が , 「新聞を取っ とか , それはどこて、どうやって維持管理さ ふつう , 生成関数はメモリをアロケート てこい」の意味を正確に知っていなければな れるのか , といったことを気にするようて、 し , それを何らかの方法て、初期化して抽象 りません。あなたと犬との間のインタフェ あってはなりません。データの集まりを維 データタイプの新たなインスタンスをひと 持管理するためには , 別の抽象データタイ っ作るだけて、す。生成関数は , そうやって イスの設計が悪かったら , 犬は新聞をびり びり引き裂いてしまうかもしれません。ど プを定義すればよいのだし , あるいはファ 作成 & 初期化したインスタンスを指すハン イルやその他の外部記憶装置とのインタフ ドル〔通常はポインタ〕を返します。この んな場合て、も , 抽象データタイプに対する ハンドルを各操作関数に渡す引数として用 インタフェイスは , く何が (what) 〉達成され ェイスもやはりそれ用の抽象データタイプ を定義すればよいのて、す〔必要な機能を何 い , またこのハンドルが , 抽象データタイ るのかを細心かっ簡潔に指定しているべき て、あり , そして , しかし , それがくどうや プのどのインスタンスを操作対象とするの もかもひとつの抽象データタイプ ( クラス ~ オ って (how) 〉達成されるのかについては , ほ プジェクト ) に盛り込むのはよくない〕。 かという識別の手がかりにもなります。 とんど明かしていないものて、あるべきて、す。 ただし , ひとつの抽象データタイプの複 ンドルの中身の構造については , 特定の想 抽象データタイプの振る舞いは , そのデ 数のインスタンスが存在て、きるのなら , そ 定をすべきて、はありませんが , しかしオプ ータタイプに対して定義する操作て、決まり ジェクト指向て、ない言語て、は , サイズがコ のそれぞれが自分のインスタンスを生成し ます。抽象データタイプの振る舞いを定義 破壊するための演算子 ( 関数 ) をそなえてい ンパイル時に一定な固定サイズのデータに する場合 , その演算子〔ないし関数〕が厳 なければならないし , またひとつひとつの するのが便利て、す。私のやり方て、はハンド ルはすべて , そのサイズがポインタのサイ 密にデータタイプの単一のインスタンスの インスタンスを明確に識別て、きるための仕 組みも必要て、す。したがって , 抽象データ ズて、す。 みを操作の対象とするならば , その演算子 抽象データタイプ「 Set 」のスケルトン ( 定義 ) List 匡ジュールを定義する 抽象データタイプという方法は , それに 固有の利点が三つあります。第一に , デー タ項目の実装詳細を隠蔽することによって , 実装を将来変更しても既存のその他のコー ドに影響がほとんど及びません。 第二に , その抽象データタイプに対する インタフェイスとして定義されている公認 の操作を介してしかデータにアクセスて、き ないようにします。副作用を少なくて、きま すし , 共通の操作を実行するコードがシス テム内のあちこちに散在したり重複して存 在することがなくなります。また , マルチ タスクを行なうシステムて、は , 抽象データ タイプはセマフォ用の便利な場所を提供し , データに対する一連の順序的なアクセスを セマフォ自身が管理するというかたちにて、 きます。つまりその場合抽象データタイプ が , 目に見えないところて、 , 抽象データタ イプの同一のインスタンスに対する複数の アクセスに対する相互排除 (mutual exclus 1 : 2 : Sets. h 3 : Set の定義モジュール 4 : 5 : 説明 : 汎用的な抽象データタイプ Set のインタフェイスを提供する . 最大サイズ MAX_CARDINALITY までの Set を作れる . 6 : Set の中には最大値 MAX ー CAR 田 NALITY 未満の無符号離散値なら , 7 : どんなタイプの値でも収められる . 8 : 9 : 1 0 : 15 : #if !defined(IMP_SETS) 16 : typedef void * HSET; 1 7 : #endif 1 9 : / * 20 : 2 1 : #define MAX_CARDINALITY 22 : 23 : #define SET_SUCCESS 24 : #define SETERR_BADHANDLE 25 : 27 : 28 : HSET 29 : unsigned short 30 : unsigned short 31 : unsigned short 32 : unsigned short 33 : unsigned short 34 : unsigned short 35 : unsigned short 36 : HSET 37 : 38 : ータイプ定義ー 一定数定義ー 65500 / / 関数は成功時に 0 を返す / / 工ラー時のノンゼロのエラーコード ープロトタイプ - SetCreate (unsigned short Size); SetDestroy (HSET (s) : SetClear (HSET (s) , SetIncIude (HSET hs, unsigned short Val); SetExclude (HSET hs, unsigned short Val); SetIsInSet (HSET hs, unsigned short Val); SetQuerYImgSize (HSET hs); SetWriteDiskImg (HSET hs, void * ptr); SetCreateFromDisklmg (void * ptr); 一定義モジュール終わり 0 抽象データタイプを c で実現 25

5. 月刊 C MAGAZINE 1993年12月号

る。だが , これはたとえば「 #include のネス トは最低 8 レベル確保すること」という規格 があり , したがって , 厳密に規格適合のプ ログラムて、あれば , # include は 8 レベルを越 えてネストしていなくてはならないという 意味て、ある。この場合 , 越えてはならない のは「最大値」て、はなく , 「最低限保証されて いるだろう最低値」ということになる。 ということて、 , この文章を見ると , 適合 (conforming) の度合いにはふたつあって , ひとつが「厳密に適合」て、あり , もうひとつ は , たんなる「適合」と呼ばれていることが わかる。前者は , 未指定 , 未定義の動作に 依存してはならないことはいうまて、もなく , 処理系定義の動作に関しても依存していて はならない。これは , 「厳密に適合」したプ ログラムの場合には最大限の移植性が確保 て、きるようにしようという発想からて、ある。 ただし , 一般に移植性を高めようとすると , 実行効率的にはペナルティを払わなければ ならないことが多いことは忘れないように 一方 , たんなる「適合」の場合には , 規格 適合の処理系が受け入れてくれるようなプ 私見て、は , たとえ「適合プログラム」とい って未指定とされているだけて、なく , 実際 ログラムて、あればよいということになる。 えども , そのような「未指定動作」に依存す これは未定義動作を引き起こさなければよ に処理系の側て、も明示的にドキュメントに るのはまずいのて、はないかと思う ( もちろ いと解釈て、きるだろう。処理系定義の機能 己されていない項目て、あるとお考えいただ ん , 「未定義動作」に依存してはならないの を使うのはかまわないのて、ある。たとえば , はいうまて、もない ) 。処理系定義動作に依存 未指定の項目は , 上記の考察にもあるよ int の値が 32 ビットだとか , short は 16 ビット するだけに留めるべきなのて、はないかと考 だという事実は処理系定義の事項て、あり , うに , 特定の処理系においても , 前後の状 況の変化に応じて , その処理が変わるかも これを利用することは一向にかまわない えている。 しれない内容を含んて、いる。したがって , もうひとつ「適合」プログラムの場合には , 名禁止の問題 それらに依存したプログラムの実行結果は , 処理系独自の拡張を利用可能という点も注 「今後の言語動向」より ほんの少しコーディングを修正しただけて、 , 意が必要だ。要するに , 「適合」プログラム 変化してしまう可能性がある。 ANSI の規格書には , LANGUAGE'€-- ト の場合には , 特定の処理系において最大の の最後の章に ( その後に LIBRARY'€—トと ところが , そのような脆弱な動作に依存 実行効率を上げ得るプログラムを想定して いうのがある ) , 「 Future language directi いることになる。その一方て、 , 移植性は明 していたとしても , 処理系はおそらく警告 ons 」という節がある ( 3.9 ) 。その中に , Fig. を発することすらないだろう。したがって , 瞭に低下することになる。 上記の定義のように「処理系が受け付けるか 5 のような文章が記されている。 ところて、 , 筆者はひとっ疑間を持ってい ることがある。それは「適合プログラム」の どうか」て、「適合プログラム」を定めるなら これは , あくまて「 Future language dir ection 」て、あり , 「今後こうする予定て、ある」 場合 , 「未指定動作」の結果に依存してかま ば , それらはれつきとした適合プログラム というだけて、ある。したがって , 今すぐ何 わないのだろうかということて、ある。ただ と分類されてしまう。これは , どうもすっ かが問題になるというものて、はないのだが , きりしないところて、ある。 - し , ここて、「未指定」と呼ぶのは , ANSI によ Fig. 4 規格準拠のプログラム ( ANS 凵 .7 COMP 凵 ANCE ( 「適合性」と訳しておく ) より ) A strictly conforming program shall use only those features Of the language and library specified in this Standard. は shall not produce output dependent on any unspecified, undefined, 0 「 implementation-defined behavior, and shall not exceed any minimum implementation limit. ( 中略 ) A conforming program is one that is acceptable tO a confO 「 ming implementation. 厳密に規格適合のプログラムか使用てきる機能は , 言語およびライプラリに関して本標準が定 めたものだけある。どのようなものであれ , 未指定 , 未定義 , あるいは処理系定義の動作といっ たものに依存した出力を生成してはならない。そして , 処理系の制限値に関する最低値の規定を , なんであれ越えてはならない。 規格適合のプログラムとは , 規格適合の処理系が受け入れることができるものである。 ( 中路 ) Fig. 5 別名禁止の問題 ( 3.9.6 Array parameters より ) The use Of two parameters declared with an array type ( p 「 iO 「 tO their adjustment tO pointer type) in separate lvalues tO designate the same Object is an obsolescent feature. ( それらをポインタ型へと読み換える前に ) 配列型として宣言されたふたつの仮引数を用いて , 同一のオプジェクトを指示する独立したⅳ引 ue として取り扱うことは , 廃止されることになる機能 である。 1 三ロ 132 ℃ MAGAZINE 1993 12

6. 月刊 C MAGAZINE 1993年12月号

0 人産 ロロ 今回は配列の話をします。配列は , イ メージ的に変数か団地に住んでいるも のなのです。変数が団地住まいとは , どういうことなのでしようか。 ー「団地住まいの変数たち」 = , , 第①回 結城浩 り返しを行う構文て、すが , for 文のような「初 ましよう。この三つの点数の平均点を計算 じめに 期化」「条件」「次の一歩」という部分はありま するプログラムはたとえば List 1 のようにな ります ( 実行結果は Fig. 1)0 せん。 早いものて、 , このプログラミングレッス List 1 を見ると , 7 行目て、国語・数学・英 のポイント ンも 9 回目を迎えました。覚えていますか , 語の点数用に変数を三つ定義 , 8 行目て、平均 4 月号の特集「ゼロから学ぶ C 言語入門」のこ 点用の変数をひとつ定義しています。それ とを。そこて℃言語というものに触れ , 自分 今月は「団地住まいの変数たち」と題して から , 10 ~ 13 行目て、それぞれの点数を代入 はいれつ て、プログラムを初めて組んだ方も多いと田 し , そして 14 行目て、平均点を計算していま 配列について説明します。 います。 9 か月が過ぎて , どうて、したか。「や 言て、いえば変数に番号をつ 配列とは , す。そして結果を参照して表示しています。 つばり難しいなあ」「けっこうワカってきた けたものて、す。変数の一種て、すから , まず printf ( " 平均点は %. If 点 *n" よ」など , いろいろな感想を持たれていると は変数についての復習から始めましよう。 heikin) ・ 思います。ぜひ , ご思見やご感想 , ご質間 変数を復習した後て、 , そこて、使った例題を この %. If というのは , 浮動小数点を小数点 をお送りください。 以下 1 桁だけ表示する形式て、す。 元に配列について説明するのて、 , 「変数なん かもう知っているよ」というあなたも , 先を 定義・代入・参照を思い出しましたか。 ューコーナー 急がずじっくり読んて、くださいね。 それて、はいよいよ配列の話に入りましよう。 先月のレッスンて、は「 while 文」について学 びました。 配列の定義 変数の復習 変数は , 名前のついた箱のようなものて、 いま国語・数学・英語の三つの教科の点 す。そして , その箱の中には数や文字を入 数のために三つの変数を別々に定義しまし れておくことがて、きるのて、した。以前この た。けれどもこの三つの変数にはすべて「点 レッスンて、学んだ「変数の三つの重要ポイン 数を表す」という共通の目的がありますね。 ト」を覚えていますか ? C 言語には , 共通の目的を持っ複数の変数に ・変数を作る ( 定義 ) 番号をつけてまとめるという機構が用意さ while 文は「条件」が満たす間だけ処理を繰 ・変数に入れる ( 代入 ) れています。それが , 今回勉強する配列な ・変数の中身を見る ( 参照 ) り返して行い , 条件が満たさなくなったと のて、す。 ころて、繰り返しを終了します。初めから「条 たとえば , 国語・数学・英語の試験があ まずは , プログラムを見ましよう。 List 2 件」を満たさなければ , 「繰り返す処理」は一 ったとします。私の国語・数学・英語の点 は List 1 を配列を使って書き直したものて、 度も行われません。 while 文は for 文と同じ繰 数がそれぞれ 65 点 , 90 点 , 75 点だったとし す。じっと見て , 何がどうなっているのか 72 C MAGAZINE 1993 12 while ( 条件 ) { 繰り返す処理

7. 月刊 C MAGAZINE 1993年12月号

C 言語プロクラミンクレッスン・ Fig. 1 List 1 の実行結果 3.0 ; 19 : ) 19 : ) Fig. 2 配列の定義 A> LCC い STI. C 11d @link. i A> LISTI 国語は 65 点 数学は 90 点 英語は 75 点 平均点は 76.7 点 凵 STI . C をコンバイル LISTI . EXE を実行 LISTI . EXE の実行結果 C のプログラム int ten [ 3 ] : 二つ用意され , それに ten という名前がつけ という配列の定義を行うと , int 型の変数が int ten[3] ; 配列の大きさは必ず定数になります。 ことを配列の大きさと呼ぶこともあります。 トなどと呼ばれます。配列の要素の個数の す。これは大カッコ , 角カッコ , プラケッ 要素の個数を括っているのはゞ [ クと、、 ] クて、 ig. 2 ) 。 て、学んだひとつの変数に相当するのて、す ( F す。そして , そのひとつの要素が , いまま のひとつひとつの住居のようなものなのて、 ようなもの , そして配列の要素は団地の中 った団地のようなものて、す。配列は団地の 0 あなたのイメージ 3 個 tenC2] ten[O] ten[l] ↑ ↑ ↑ を調べてみましよう。 配列の例 : 配列を定義する 素の個数て、す。配列は何個かの要素が集ま 次に書かれている [ 3 ] の部分は配列の要 す。 ten という名前の配列を定義しているわけて、 次に書かれている ten は配列の名前て、す。 義してます。 型を持ちます。ここて、は整数型の配列を定 します。配列は変数の一種て、すから , 必ず 初めに書かれている int は , 配列の型を表 int ten[3] ; て、配列を定義しています。 ま iList 2 の 7 行目を見てください 国語・数学・英語の平均点を計算する このひとつひとつが int 型の変数となる 数を代入してみましよう。 の要素のそれぞれに国語・数学・英語の点 配列 tenC3] には三つの要素があります。そ 今度は配列への代入を考えてみましよう。 配列の例 : 配列に代入する これが , 配列の定義て、す。 ten[0] ten[l] ten[2] 90 ; 75 ; られるのて、す。 この配列の大きさは 3 て、す。 日本語て、書くと , それぞれ , ・ 0 番目の要素に 65 を代入 ・ 1 番目の要素に 90 を代入 ・ 2 番目の要素に 75 を代入 という意味になります。 2 : 4 : 7 : 8 : 9 : 12 : 14 : 15 : 16 : 1 : #include く stdio. h> 3 : void main(void); 5 : void main(void) int kokugo, List 1 を配列を使って書く List float heikin; int tenC3] ; 5 : void main(void) 3 : void main(void); 1 : #include く stdiO. h> suugaku, eigo = 75 ; suugaku = 90 : kokugo = 65 ; float heikin; eigo; (kokugo + suugaku + eigo) / printf(" 国語は Xd 点 \ 心 , kokugo) ; printf( ”数学は Xd 点 *n", suugaku) : printf( ”英語は Xd 点 \ n ” , eigo) : printf( ”平均点は %. If 点 %n", heikin); heikin 2 : 4 : 7 : 8 : 9 : 11 : 12 : 14 : 15 : 17 : tenC0] = tenC1] ten[2] = heikin C 言語入門講座 73 printf(" 平均点は %. If 点 *n", heikin) : printf(" 英語は Xd 点 \ n ” , tenC2]) ; printf( ”数学は Xd 点 \ n ” , tenCI]) : printf(" 国語は Xd 点 \ n ” , ten[0]) ; = (tenC0] + tenC1] + tenC2]) / 3.0 ; = 90 :

8. 月刊 C MAGAZINE 1993年12月号

らも「明確には決まっていない」という意味 65535 という unsigned int の値になる ( 詳細は 入ることになるため , 結果的にこれは長さ 0 の文字列として取り扱われることになるだ としか受け取れない。 ANSI には , もうひと 本連載第 17 回 ( ' 93 年 2 月号 ) 参照 ) 。したがっ つ implementation-defined という用語もあ て , る ( 実は , この用語も本連載て、何度か登場し 文字列定数は書き換え可能か ている ) 。 ANSI て、はこの三つの用語は明白 に定義され , きちんと使い分けられている もうひとつ , かっての C プログラミングの else 「奧義」のひとっとして , 文字列定数をプロ (Fig. 2 ) 。実際には , それぞれの後に「 beha printf ("K&R*n") ; vior ( 動作 , 振る舞い ) 」という言葉をつけ グラム実行中に書き換えるという技が使わ という比較を行うと , 両者て、は答えが正反 て , それを専門用語としてとくに定義して れていた。たとえば , UNIX の mktemp とい 対になる。本項と前項は , Quietchange の いるのて、ある。 なかて、も , 微妙て、気づきにくく , それて、い う関数を , 規格を読む場合 , とくにコンパイラを実 て決定的な差異を生み出すものだと思う。 char * tmpfp , 装しようとか , 非常に移植性の高いコーデ 現実に何件かこれらの Quietchange が原因 tmpfp = mktemp("/tmp/tmpXXXXXX") : イングをしたいと望む場合には , これらの て、問題となったケースを見たことがある。 区別が重要て、ある ( 実は , さらにもうひとつ 注意が必要て、ある。 のように呼ぶことは , 割りとよく用いられ locale-specific というのがあるが , これはそ るコーディングて、あった 8 進数の数字として“ 8 ″と駅 9 ″は 許されないことになった の名のとおり locale がらみて、あり , locale に そうすると , mktemp という関数は引数に 渡した char * がポイントする文字列の中身 ついては本連載の範疇て、はないのて、 , これ これは教科書のほうの K & R の第 2 版を見る には触れない ) 。 を書き換えて戻してくるのて、ある。だから , と「みんなが気にいる , ちょっとした変更 (Everyone's favorite trivial change) 」な この例て、は $/tmp/tmpXXXXXX" という文 端的にいえば , unspecified とは , 正しい プログラム / データて、あっても , 特定の状況 字列定数の値が実行するたびに書き換ると どと記してあるが (K&R 2nd, Appendix. において , 厳密に何が起きるのかを規格は C) , そんなに現実は甘くないと筆者は思っ いうことて、ある。 「指定しない」という意味て、ある。一方 , un このようなコーディングは , ANSI 環境て、 ている。 defined とは , プログラム / データに誤りがあ は動作しないかもしれない。なぜならば , これに関しては , そもそも古いコンパイ った場合に , その結果がどうなるかを規格 ラが、、 8 〃や、、 9 〃を「 8 進数字」として許していた ANSI て、は文字列定数は書き換え不可能なメ モリ領域に置かれるかもしれないからて、あ は「定義しない」という意味て、ある。 ことのほうがおかしいともいえる。だが , これらはいずれも , 「指定・定義しないの 許していた以上 , それを悪用した て、 , 処理系の側て、臨機応変に処理してかま 080 ” undefined と わない」ということて、あり , かっ , ひとつの という文字列定数を含むソースがあるだろ ilnspecified の差 処理系て、あったとしても , 常に一貫して同 うことは想像に難くない じ動作を行う必要はないということて、ある。 本連載のなかて、 , これまて、何度も ANSI 規 古いコンパイラて、は , これは、、 \ 080 〃とい これに対して implementation-defined と う 1 文字からなる文字列定数て、ある ( 当然 , 格の原文を引用してきた。そのなかに , un いう用語は , unspecified 同様 , 正しいプロ defined と unspecified というふたつの用語が コンパイラによって末尾に NUL が付加され グラム / データの動作の話なのだが , 処理系 登場したのにお気づきて、あろう。これらの る ) 。そして , その文字コードは 080 すなわ 用語は , それぞれ登場した時点て、簡単に意 の実装ごとに処理系製作者によって定義さ ち , 8 進て、書けば 0100 という値 , 10 進て、書け れているような動作て、ある。当然 , 処理系 こうして改め ば 64 て、ある。ところが , ANSI て、は上記のよ 味を説明してはいる。だが , が違えば異なっているかもしれないが , ひ てふたっ並べてみると , 両者の意味の違い うに、、 8 〃は 8 進数字て、はなくなった。したが とつの処理系のなかて、は一貫した動作にな がよくわからなくなるのてはないだろうか。 って , この文字列は , っていなければならない 響 0 ” ' 名 0 ” undefined まだわかりにくいと思うのて、 , もう少し ーー未定義 , あるいは不定 と書いてあるのと同じに解釈される。 具体的に見てみよう (Fig. 3 ) 。 すなわち , 先頭に NUL の文字コードが , unspecified 秩序のある順て並べれば , 次のようにな 引き続いて、、 8 〃と、、 0 〃の文字コードが含まれ 未指定 , あるいは明示しない るだろう。 原語を見ても , 日本語訳を見ても , 一見 る , 長さ 3 の文字列定数だと解釈されるのて、 処理系定義 (implementation-defined) して区別をつけるのは難しいだろう。どち ある。しかも , 不幸なことに先頭に NUL が if (0xffff > 0 ) printf ("ANSl*n") ; 130 C MAGAZINE 1993 12

9. 月刊 C MAGAZINE 1993年12月号

0 番目って変ないい方だなあ , と感じられ るかもしれませんね。 C 言語の配列は必ず 0 番目から始まります。 1 番目から始まるのて、 はありません。これは慣れないと間違える 点なのて、注意してください 0 番目ら始まる : と 0 、う = とは・・ そう , 要素の個数カ 3 個の場合には , ten[3] は使ってはいけないことになります。 int t en [ 3 ] ; として定義したのて、 , ついうつかり ten [ 3 ] も使えるような気になってしまいそ うて、す。けれど , 定義て、書いた 3 は要素の「個 数」を表しているのて、あって , ten [ 3 ] まて、 使えることを表しているのて、はありません。 ten という団地を作るときに三つ分の住居 しか作らなかったようなものて、す。 ten [ 0 ] は 1 個目 , tenC1) は 2 個目 , tenC2] は 3 個目 て、す。定義した要素はこれて、すべてなのて、 す。 tenC3] は 4 個目になってしまいます。 つしか住居がない団地の 4 個目の住居に住み たいといってもそれはて、きません。 先ほど私は「配列への代入」と書きました が , 正確には「配列の要素への代入」と書い たほうがよかったかもしれませんね (Fig. 3 ) 。 それから , [ と ] の中に書かれた数のこと を添子といいます。英語て、は , index( イン デックス ) といいます。 配列の例 : 配列を参照する Fig. 3 配列の要素への代入 あなたのイメージ 65 90 7 5 C のプログラム ten [ 0 ] tenC1] ten [ 2 ] 5 0 5 9 1 ′ 0 ten [ 0 ] tenC1] tenC2] Fig. 4 配列の要素の参照 C のプログラム printf("%d*n" printf("%d*n printf( ” %d*n" あなたのイメージ 90 7 5 の 0 の 75 65 90 ten[O] ten[l] ten[2] ( 箱の中身か消えるわけではない ) 定義 , 代入ときたら次は参照て、す。もう 配列と変数の比較 TabIe 1 おわかりて、しよう。配列の要素の値を参照 したいときには , 代入のときと同じように 配列名 [ 添字 ] と書けば結構て、す。 List 2 て、いえば , 配列の 要素同士の足し算をしたいときには , ten[0] + ten[l] + ten[2] と書きました。またそれぞれの点数を表示 したければ , printf ( " 国語は %d 点 *n' printf ( " 数学は %d 点 *n", ten[l]) ; printf ( ' 英語は %d 点 *n", ten[2]) ; このあたりは普通の とすればいいのて、す。 変数と同じて、すね ( Fig. 4 ) 。 変数 配 列 定 義 int ten[3] , int kokugo' suugaku, eigo , 65 , 90 , kokugo suugaku 90 ten [ 0 ] 代 入 ten[l] ten[2] 5 0 5 6 9 「′ printf("%d*n ” kokugo) : p 「 intf("%d*n" suugaku) : printf( ” %d*n ” , eigo) ・ ( 配列名 [ 添字 ] が変数名と同じ扱いを受けるのがわかる ) printf( ” %d*n ” te n 照 printf( ” %d*n" 参 ten printf("%d*n ” te n 74 C MAGAZINE 1993 12

10. 月刊 C MAGAZINE 1993年12月号

く 7 〉 Browse POINTS. BSC 0 0 ■ー 0 」 Fig. IO プラウザ 便 能ー G 猷 0 Definition 他人のプログラム ( あるいは AppWizard の 作ったスケルトン ) などを見ていると , 関数 の実体がどこにあるかわからず苦労するこ とがよくあります。 VWB て、は , 定義を見た い関数名にカーソルを合わせ , Browser/G otoDefinition メニューあるいは亘を押す ことにより , 定義している場所へ飛べます ( ただし , プラウザデータベースを作成して おかねばなりませんが ) 。 こまて、はありがちな機能て、す。 VW indows 上の環境版て、も , コンパイルには D リコンパイルドへッダ ( stdafx. cpp のコンノヾ B て、はさらに , 型の定義まて、調べてくれま OS 上のものを使用するようて、す。 Project/ イル時に作られる 800K バイトくらいのもの ) す。それも , クラスや構造体の定義だけて、 Build メニューなどを選んて、コンパイルを始 の作成がありますから , まあ仕方ないて、し はなく , 単なる typedef, 果ては #define まて、 めると , < Output > というウインドウがオー もが守備範囲て、す。 Fig. 11 は , TRUE の定 プンし , 処理の状況を刻々と表示します。 そもそも , コンパイル時間はそんなに遅 義を探したところて、す。 TRUE の実体は実 ただ , 英語版 VC 十十の場合 , このコンパ くないのかもしれません。そう , とにかく は # define だったのてすが , 見事に探しあて イル機構が日本語 Windows とは相性が悪い 遅いのは , リンクとプラウザデータベース ようて、 , コンパイルを始めた瞬間 , 画面に の作成なのて、す。 てくれました。 これは超便利て、す。惜しむらくは , 変数の 極彩色のロールシャッハテストを表示しつ AppWizard て、スケルトンを作って , その 定義場所まて、はサポートされていないこと ままコンパイルしたとしましよう。て、きた つ , お亡くなりになってしまいます。ふと て、す。これも調べてくれると , ファイルのサイズを見て , 最初は我が目を さらに便利 、、 WinTee" という言葉が頭をよぎったりし になることて、しよう。 こて、深く触れません。もちろん , ますが , 疑いました。何しろ , EXE ファイルが 1 .2M 英語版 Windows て、は問題なく動作していま バイト ( K バイトじゃないて、すよ ) もあるのて、 すし , 日本語版 VC 十十て、はそんなことはな す。そりや時間がかかっても仕方ないて、す くなるて、しよう。 ね ~ と妙な納得をしたのて、すが・・ くだん 言語仕様などの話は , 本誌 ' 93 年 6 月号の なみに , 件の EXE ファイルをデバッグて、は 遅いピルド なくリリースパージョンとしてビルドして 速報を読んて、いただくとして , こて、はイ 余談はさておき , とにかくビルドが遅い 75K バイトになりました。なんな ンプレッションを書いてみたいと思います。 みると , の , この差は。 のて、す。初回のコンパイルて、は , 巨大なプ VC 十十は , Borland C 十十と異なり , W Fig. 12 開発手順 Fig. 1 1 TRUE 定義の探索 VW B Eile Edit ew Project ßrowse Pebug 1001s Qptions Window Help AppWizard く 9 〉 F:iUSRiSRCiCMWCP14POlNTSiPOIN1 、、 M.CPP く 11 > F. US R C VCPI INTS CPOINTS. H 第 ypedef unsigned long 0 RD ー / / 32 ー 01t リ n519ne0 nunDer 物 ypedef int 日 00 し ; / / 800 L ean ( 0 0 ド ! 0 ) / / を・ pointer tO ヨ 5む・ ing リ p を 02 char FRR• LPSTR; t リ p を 02 ー const char FAR ・ LPCSTR; / / far pointet ・ tO ヨ read-onl / / む、 t ド act itet ・ ation positio ypedef 聞 id ・ POSITION; / Standa•・ 0 constants undef FALSE undef TRUE 物 und ~ 楸ル L efine FAL SE 0 define 1 efine ル L 0 ( みツ / Diaqnostic suppot ・ t T 0 : D eriyed ロ a33 6 raph 宀ト CP0ints CPtrList CObList E お d T 肥砂 ◆ CPoints: :Draw(c1ass CDC 善 ) CPoint CStringList CMapWordToPtr CMapPtrToWord CMapPtrToPtr Definitions 0 CPoints f : 、 us ド、 5 ド c 、 c リ cp12 、 points 、 cp Re 薈 2 ド nce tO Point 0 コ、ノラ Mi ロ 050 Ⅵ su Cf+ ・ POINIS. MAK 完成版の コンノヾイラ AppStudio ClassWizard Debugger く fl 〉 F:\MSVC\MFC\INCLUDEV.FX. 日 M i ロ 05 0 V i 5 u 引 C , ー PO ー N ー S. MAK Fig. 13 ツールノヾー 日 Edit VieW Project ßrowse Debug 工 00 Qptions Window 旦 e ゆ ステータスパー NUM 74 2 NUM 00082 009 42 C MAGAZINE 1993 12