_open #include #include CTurboC] く iO. h 〉 く fcntl.h> handle buffer bytes ・機能 ラ気プリ 読み出すファイルのハンドル 読み出すデータを格納するバッファ 1 回に読み出すバイト数 int —open(const char * path, path 作成すみファイル / 、ンドルへのノヾス mode アクセスの許可設定 ・機能 int mode); システムコール Ox5D を実行し , 引数 path に指定したファイルをオープンする。 引数 mode には , ファイルのアクセスモード , シェアリングモード , インヘリッ ドモードを , 次に示す記号定数を用いて指定する。複数のモードを組み合わせ るときは , それらの記号定数の論理和をとる システムコール Ox3F を実行し , 引数 handle に指定したファイルから , bytes に 指定したバイト数のデータを読み出し , buffer が指定するバッフアへ読み出し たデータを出力する。なお , この関数は Ox la をファイルの終端として認識する ー戻り値 正常に読み込んだときは格納されたバイト数。ファイルの終端を読み込んだ場 合は 0 を返す。読み込みに失敗すると一 1 を返し , グローバル変数 errno に次のい ずれかの記号定数を設定する EBADF ファイルハンドルが無効です EACCES アクセス不可 ( han 引 e に対応するファイルは読み出し不可としてオ 定数 O—NOINHERIT O—DENALL O—DENYWRITE O—DENYREAD O—DENYONE ■戻り値 意味 子プロセスには継承しない 現在のハンドルだけがそのファイルにアクセスできる ファイルに対する読み出し用オープンのみを許す ファイルに対する書き込み用オープンのみを許す ファイルに対するほかのシェアリングモードを許す rename #include #include ■型 ープンされている ) CMS-C,Tu rboC] く io . h > CMS-C] く stdio. h> オープンできたときは 0 , オープンできなかったときには一 1 を返し , グロ ル変数 errno に次の記号定数のいずれかを設定する int 「 ename(const Cha 「 * oldname, newname); oldname 元の名前へのポインタ newname 新しい名前へのポインタ ・機能 cinst Char 日 N VACC ENOENT EMFILE EACCES アクセスモードが正しくない バスもしくはファイルが見つからない オープン中のファイル数が多すぎる アクセスできない dos_read CMS-C] #include く dOS. h 〉 unsigned —dos—read(int handle, VOid far unsigned count, unsigned * bytes); * buffer, oldname で指定したファイル , またはディレクトリ名を newname で指定した名 前に変更する 引数 newname で異なったバス名を指定することにより , ファイルをあるディレ クトリからほかのディレクトリへ移すことができる。ただし , 異なるデバイス へ移動することはできない。また , ディレクトリ名は変更することはできない ・戻り値 操作が成功した場合は 0 。工ラーが発生した場合は一 1 を返し , errno に以下のい すれかを設定する handle buffer count bytes ■機能 読み出すファイルのハンドル 読み出すデータを格納するバッファ 1 回に読み出すバイト数 実際に読み出したバイト数 システムコール Ox3F を実行し , 引数 handle に指定したファイルから , count に 指定したバイト数のデータを読み出し , buffer が指定するバッフアへ読み出し たデータを出力する。引数 bytes が指すバッフアには , 実際に読み出したバイト ■戻り値 数が入る EACCES アクセス不可 ( han 引 e に対応するファイルは読み出し不可としてオ EBADF ファイルハンドルが無効です 返し , グローバル変数 errno に次のいずれかの記号定数を設定する 正常に読み出したときは 0 。読み出しに失敗すると , MS-DOS のエラーコードを EACCES ENOENT EXDEV ENOTSAM ・型 #include #include remove newname によって指定したファイル , またはディレクトリがすで に存在する。またはバスの指定が無効。もしくは , oldname がデ ィレクトリであって newname が異なるバス名を指定している 0 旧 name で指定したファイル名またはバス名が見つからない ファイルを異なるデバイスに移そうとした CMS—C] 同じデバイスではない CTurboC] CMS-C] く stdio. h> く io. h 〉 CMS-C,TurboC] ープンされている ) rea d CTurb0C] #include く iO. h > ・型 int —read(int handle, bytes) ; void far * buffer, unsigned int remove(const char * path); path 削除するファイルのバス名 ■機能 path で指定されたファイルを削除する ■戻り値 ファイルが正常に削除されたとき 0 。工ラーの場合は一 1 を返し , errno に以下の C プログラマのためのランタイムライプラリ入門 ENOENT ファイル名またはバス名が見つからない る EACCES バス名がディレクトリまたは読み込み専用ファイルを指定してい 値のうちのいずれかを設定する 95
i09 0055 薈 0 「 ( 十十 A Debug\: List 3 ている。 assertSet, traceSet, error Set, inf0Set はアクセス関数て、ある。オプ ジェクト指向プログラマにとって , アクセ ス関数を使用するかしないかという問題は , 宗教的な問題 (religious matter) て、ある。あ るものは信じて使用し (swear (y) , またあ るものはまったく同調しない (others (t) 。 この問題に対してプログラマはどのような 立場をとるかを決心しなければならない 私といえばアクセス関数を使用するほ うの立場だが , 読者のみなさんを同じ立場 に転向させようとまて、は思っていない assert マクロは Trace クラスのあとに定 義している。これは簡単なマクロて、あるが , パラメータ condition を文字列に変換する文 字列化オペレータ ( # ) を含んている。ただた んにマクロバラメータを引用符て、囲むとい う古い方法は , より新しいコンパイラて、は 動作しないものがあるかもしれない ヘッダファイルの残りは , クラスのイン プリメントの始めの部分て、ある。可能なか ぎりの関数をインライン関数として作成し たのて、 , デバッグコードは最小のオーバー ヘッドをもつだけて、すんだ。トレースのオ ーバーヘッドは , トレース出力が禁止され , コンノヾイラが inline 孑旨令に従っているとき , ほとんどのトレース呼び出しの AND 演算や 分岐命令からなる。 ファイル trace. cc(List4) には , スタート アッブオプジェクト initTraceMode の宣言 を含んて、いる。これは , どのプログラムて、 もこのモジュール以外て、は必要て、なく , 外 部シンポルテープルに存在している必要が ないのて、 , スタティックとして定義してい スタートアッブオプジェクトのクラスの コンストラクタは , operator( ) の 2 番目の こて、定義され バーション定義とともに る。以前にも述べたとおり , このバージョ ンは可変個数の引数をとるためインライン 関数として定義していない。したがって , #condition) : \ い NE trace. d0Assert(--FILE 32 : 33 : } 34 : 35 : static const int incr 37 : enum { 38 : 39 : 40 : errorMask 44 : 45 : i n ⅱ ne 46 : Trace: :Trace(const char *name) if (traceSet()) { indent() : fputs (name, stderr) : fputc ('Yn' stderr) : 52 : I eve ー十ニ i ncr : 56 : i n ⅱ ne 57 : Trace: : Trace (void) 58 : { 59 : lncr; 62 : inline Trace& 63 : Trace: :operator() (const char *str) i f (traceSet ( ) ) { indent() : 66 : fputs (str, stderr) : fputc ( ・ Yn' stderr) : 70 : return *this; 73 : i n 1 i ne vo i d 74 : Trace: :doAssert(const char *fi le, int line, const char *msg) if (assertSet() ) { indent() : fprintf(stderr, "Xs(Xd) : assert Y"XsY" failedYn" file, ー ine, msg) : 86 : inline void 87 : Trace: : indent (void) for (int i level; fputc ( ' stderr) : 94 : i n ⅱ ne i nt 95 : Trace : : assertSet ()o id) return traceMode & assertMask : assertMask = 0X01 , traceMask = 0X02. = 0X04 , infohask = 0X08 40 CMAGAZINE 19 4
List 3 trace( type, fmt, という呼び出しを使用するときのオーバー ヘッドには , 手続き呼び出しも含んて、しまう。 Trace は , 大きなプログラムのなかのバ グを追跡するのを助ける。プログラムを中 断する箇所を知るためのテストを行うとき , トレース出力をファイルにリダイレクトし ておけば , 手続き呼び出しの完全なテスト トレース情報を得ることがて、き , プログラ ムをいちいち止める必要はなくなる。プロ グラムのなかてトレース出力を許可したり , 禁止したりて、きるということは , テストト レース情報を適当なレベルの量に保っため に必要な機能て、ある。ソースレベルのデバ ッグに比べれば見劣りするかもしれないが , Trace クラスて、用いたデバッグて、も , 難解 なバグを追跡するのに必要な情報をたくさ ん得ることがて、きる。 AT&T から C 十十 Ver. 2 . 0 がリリースさ れているが , C 十十の互換性は一時的流れの なかにある。つまり , 新しい標準が定義さ れたが , まだ多くのサイトて、は用いられて いない状態て、あるといえる。今回開発した Trace は Free Software Fundation の G 十十 Ver 1.35.00 ( パッチ入り ) により記 述した。このバージョンは , Ver. 2.0 の特長 の多くといくっかの拡張を含んだものて、あ る。しかし , 私が用いたヘッダファイルは , C 十十に変換した私のマシンの C のヘッダの 自家製改良バージョンて、ある。一方 , stdarg. h などはゼロから作ったものなのて、 ( 全部て、 4 行 ) , 完全なものとはいえない Marco S. Hyman 氏は 1969 年以来 , コンヒ。 ュータとともに楽しんている人物てある。 彼は , カリフォルニア , サンフランシスコ にある会社て、ソフトウェアを設計しコーデ イングする中心的工ンジニアてある。彼の 趣味は C 十十とオプジェクト指向プログラミ ングて、ある。 i n ⅱ ne i nt 100 : Trace: : traceSet(void) 101 : 102 : return traceMode & traceMask : 103 : 104 : } 105 : i n ⅱ ne i nt 106 : 107 : Trace: :errorSet(void) 108 : { return む aceMode & errorMask : 109 : 1 1 0 : } 】 1 2 : i n ⅱ ne i nt 113 : Trace::infoSet(void) 1 14 : { return traceMode & infoMask; List trace. CC #include "trace. ド 1 : 2 : # i ncl ude く std ⅱ b. h > 3 : 4 : static Trace initTraceMode; 5 : / * i n i tT raceM0de に使われるコンストラクタ。 * / 6 : / * 環境に debugMode があれば , raceMode を設定する。 * / "DEBUGMODE ” : 7 : static const char *const debugMode 9 : Trace::Trace() char *modeVal 12 : if (modeVaI) { setTraceMode(at0i (m0deVaI)) , 14 : 18 : static const char *const id[] e 「 ro 「 : 20 : ” i n fO : 22 : 23 : static int mask[] 24 : errorMask, 25 : infoMask 27 : 28 : Trace & 29 : Trace: :operator() (TraceMode traceMode, const char *fmt, if (this->traceM0de & mask[traceMode]) { 32 : va_l ist args: 33 : indent() : 34 : fputs (id[traceM0de], stderr) : 35 : va_start(args, fmt) : 36 : vfprintf (stderr, fmt, args) : 37 : ・ stderr) : fputc ( ・ \ n ・ 38 : 39 : return *this; 42 : 43 : / * スタティックメンパの traceMode を設定する。環境変数によるデフォルトの * / 44 : / * 設定値は , アプリケーション側の中からこの関数を呼び出すことで上書きできるり・ 45 : Trace & 46 : Trace: :setTraceMode(unsigned int traceMode) this->traceMode traceMode : return *this; getenv(debugM0de) : C 十十のデバッギングクラス 41
五ロ 用 応 したい場合には , キーの割当てを変更して 第 1 バイトにそれ以外のコードを割当てる必 要があります。なぜなら ESC コードが入力 されてきたときに , ~ キーによる入力 なのか , ファンクションキーによる入力な のかが判断て、きないからて、す。 たとえば , PC -9800 て、は , ファンクション キー , 亠 , の各キーが ESC コー ドて、始まります。たとえば , ・亠 ( C 叩 y AII) て、は , テンプレートの文字列をコヒ。ーし ますが , 亠を押す代わりに ~ + と入力しても同じ機能が働きます (Fig. 2 ) 。 今回作成するキー入力関数て、は , 亠 とのキー割当てを変更しています ( フ アンクションキーなどの割当ても , 同様の 方法て、変更することがて、きます ) 。 キーの割りつけ - asgn 関数 ) ます , キーの割りつけを行います。例と して PC ー 9800 シリーズを取り上げます。 前述したように , ESC コードて、始まるキ ーを入力すると ESC との判別がて、きないの て、 , ESC コードて、始まるキーの割りつけを 変更してしまいます。ここて、は , キー入力 に直接関係する亠との割りつけ を変更します。 今回紹介するプログラムには記述してい ませんが , ファンクションキーやそのほか のキーも , 同様の方法て、キー割当てを変更 することがて、きます ( 詳細は , プログラマー ズリファレンスを参照してください ) 。 注意事項としては , プログラム中てキー 割当てを変更したら , OS へ復帰するときに は必ずキー割当てを元の状態に戻す点てす。 これは , キー割当てだけに限らず , 画面 , プリンタなど OS 環境のすべてに当てはまり ます。 現在市場に出回っているアプリケーショ ンのなかにはこのルールを守らないものも あり , そのため後から起動するソフトウェ アに原因不明の誤動作が発生してしまいま す。 OS は , 共有の環境てすからプログラム 内て変更した場合には , 必ず後始末をして から終了するように心掛けてください 応用 C 言語 113 List 2 / * 半角漢字定数 for HANKJ3 * / / * 半角漢字定数 for HANKJ4 * / / * 半角漢字定数 for その他 * / / * SPACE * / / * DE し * / / * Return * / / * BUZZER Asci i control COde / * HOME_CLR * / / * 漢字第 1 バイト * / / * 漢字第 2 バイト * / 0xlf 0X20 0X02 12 : #define CONSTI 13 : #define CONST2 14 : #define CONST3 16 : #def ine B し ANK 17 : #define FWD 18 : #define BACK 19 : #define INS 20 : #define DE し 21 : #define ESC 22 : #define CR 23 : #define BEL し 24 : #define HOME_C し R 26 : #define ANK 27 : #define KANJII 28 : #define KANJ12 29 : 30 : 31 : / * get-lchar 32 : *ichr) get-lchar(unsigned Char 33 : i nt 34 : { 35 : i nt rc , 36 : uns igned Char 37 : *ichr=0; 38 : *(ichr + 1)=0; 39 : 40 : begin: *ichr=getch() : if(*ichr く B し ANK) 42 : 43 : switch(*ichr) 44 : 45 : case BACK: 46 : case FWD: case CR: 48 : 49 : case INS: case ESC: 50 : case HOME_CLR: 52 : rc=*ichr; 53 : break; 54 : default: buzzer ( ) : 56 : goto begin; 58 : 59 : 60 : 62 : 63 : 64 : 65 : 69 : 70 : 77 : 79 : 80 : 82 : 84 : 0x0c 0X08 0X12 0x7f 0x1b 0x0d 0X07 0xle 0 1 2 hanknj; 1 バイト入力 * / / * 制御コード * / =0xfe)) else if((*ichr>=0x80 & & *ichr<=0x9f)ll(*ichr>=0xe0 *ichr く / * 全角文字 * / / * 漢字第 2 バイト入力 * / *(ichr + l)=getch() : / * 半角漢字コード * / if(*ichr==0x85) hanknj=*(ichr + I); if((hanknj く HANKJI) & & (hanknj>HANKJ2)) buzzer() : goto begin; else if(hanknj く HANKJ3) hanknj-=0xIf; else if(hanknj く HANKJ4) hanknj-=0x20; e I se hanknj + ニ0X02: *(ichr)=hanknj; / * ANK に変換する * / *(ichr + l)=o; rc=ANK; e ー se rc=KANJI 1 : else if(*ichr==DEL) rc=DE し : e ー se rc=ANK; return(rc) :
char * gets( char * s ) ; 行末の改行は、、¥ 0 クに置き換えられま す。戻り値は s て、あり , ファイルの終わり , またはエラーのときは NULL が返されま す。 02 puts puts は , 標準出力に文字列 s と改行を出力 します。書式は以下のようになります。 int puts( const char * s ) ; 戻り値は , 負て、ない整数を返します。工 ラーのときは EOF を返します。 List 8 1 : #include く stdio. h> 2 : 3 : #define MAX_RANGE 256 4 : 5 : VO i d ma i n ( vo i d ) char s[ MAX-RANGE ] : 7 : 8 : while ( fgets( s, MAX_RANGE, 9 : stdin ) = N 此し ) fputs ( s, stdout ) : List 9 す 士 6 ぎ す 多 、カ 数 1 よ > CO っ 0 ・ , 0 1 ム 4 し 0 ・ 1 0 / ` 14 十》↓し 0 0 ・ーよし ー 0 ・ 0 + レ ・ -1 っ 0 -4 戸 0 6 ー 8 0 , 1 ワっ -4 - -0 CD 行ー 8 9 1 ー・、ー人 11 1 ・よ 1 1 ー - ・ーよ、ー 11 1 ・・ ( 2 ) stdlib. h stdlib. h は STanDard LIBrary Header の略て、 , 標準的な , ユーティリティ関数な どがプロトタイプ宣言されています。 ( ① atOi atoi (Ascii TO lnteger) は , 文字列 s を int に変換します。書式は以下のようになり ます。 int atOi( const char * s ) ; ② atol at01(Ascii TO Long) は , 文字列 s を long に変換します。書式は以下のようにな ります。 long atOl( const char * s ) ; List 1 0 1 : #include く stdio. h> 2 : #include く stdlib. h> 3 : 4 : void main(void) i nt i : 6 : 7 : printf("srand を用いないときの疑似乱数整数。 Yn"); 8 : for ( i 9 : i , rand ( ) ) : printf( ” X2d : %u%n" printf("srand を用いたときの疑似乱数整数。 Yn"); i く = 10 : i + + ) { for ( i srand( i ) : 14 : printf("%2d : %uYn ” 16 : ③ atof at0f(Ascii TO Float) は , 文字列 s を double に変換します。書式は以下のように なります。 double atOf( const char * s ) ; , こて、 , ① , ② , ③を代表して , ①の atoi の例をあげておきます。 List9 はビープ音を 鳴らすプログラムて、す。コマンドラインか ら , 引数としてビープ音を鳴らす回数を指 定することがて、きます。もし , 引数を指定 しない場合は , 1 回だけ鳴るようにしてあり ます。たとえば , A> List9 10 とすれば , 10 回ビープ音が鳴ります。 12 行 i , rand ( ) ) : List 1 1 1 : #include く stdio. h> 2 : #include く stdlib. h> 3 : 4 : vo i d ma i n ( vo i d ) system("dir/w") : 6 : 124 CMAGAZINE 19 4
C 十十が強力なのは , それが , プログラミングとい うものに対して , これまでとは違った新しい見方を 私たちに提供してくれるからです。その新しい見方 は表記法に表れており , なかでも演算子の意味や機 能をプログラマが拡張できる「演算子オーバロード」 LANGIJAGE 4 12 : } : LANGUAGE 提携記事 「文字列をノテクスする配列」 を C 十十で実現 String-Inde ed A 0Y5 in ( 十十 eff Tay10 「 / 岩谷宏訳 OMPUTER LANGUAGE/Dec. 1989 ) C 十十で特徴的な演子オーだロードにより , シ ンボルテープルを文字でノテクシングする配 列のように扱うことかで寺。 C 十十の場合 , 浮動 小数点数 , ユーサ定義テータ型 , 不連続的な整数 値域などか含まれる配列も , それらのインプリメ ントは隠蔽されているので , プログラマからは内 部の構造を意識しない単なる配列として取り扱う ことかできるからだ。本稿では , 文字列でインテ クシングする配列をバイナリサーチとハッシュテ ープルで実現する方法を紹介する。 シンポルのテープルを作って , それを何らかの lookup ( ) 関数で参照するという方法は , どのプロ グラマにとってもお馴染みのものですが , C 十十では 演算子をオーバロードすることによってシンポルテ に扱うことができます。こういう考え方は , MUMPS ープルを , 文字列でインデクシングする配列のよう が特徴的です。 List 1 [ い stl-a] [ い stl-b] lookup(symbol, S s->type keyword : / * または * / "const") : keyword : symbOl ["const"] . type keyword; const")->type lookup(symbol, bst. hpp 1 : / / bst. hpp 3 : # ifndef _BST 4 : #define _BST 1 struct bst_nodes { bst-nodes *left, *right; char *string; bst-nodes (char * ) : -bst-nodes() : void forall (void ( * ) (bst_nodes * , ostrean & ) , friend bst_nodes **lookup(bst nodes * * , char 13 : #endif 9 : 8 : 7 : 6 : 5 : 2 : List 2 ostream & ) : 5 9- dexed A 汁 0Y5 in ( 十十 「文字列でインデクスする配列」を C + + で実現 29
Trace オプジェクトにはローカル格納を含 んて、いない。 Trace クラスのデータメンノヾ はスタティックだけて、ある ( これはクラスの すべてのインスタンスにより共用されるた めて、ある ) 。そのうち , データメンバ level は 現在のインデントレベルを保持するものて、 1 : A>set DEBUGMODE=15 ある。もう一方の traceM0de はトレース出 カ許可ビットを保持するものて、ある。両者 ともクラスのプライベートメンバて、ある。 クラスはふたつのコンストラクタをもっ ている。ひとつめのコンストラクタは , ク ラスを初期化するのに用いるものて、ある。 A> t e s t : ma ー n i n { 0 : i n f 0 : i n f 0 : t e s t. g a 「 b a g e Th e a d d 「 e s s 0 f Th e a d d 「 e s s 0 { Th e a d d 「 e s s 0 { CPP(44) : a S S e 「 t t 「 a c e i s 2A62 c i s 2A 6 2 0 p t i nd i s 0 p t i れ d 1 8 6 「 g C 06 を用いて { a i le d ヨ 0 0 pC h a 「 e n t e 「一 n g ー 0 0 p Th i s i s a t e s t Th i s i s a t e s t e x i t i n g ー 0 0 p G 0 0 d ト y e ( 訳注 . PCー9801VM2 で Zortech C 十十 コンパイルした実行例 ) LiSt trace. h verl . 2 : 3 : 4 : 5 : 6 : 8 : 9 : 10 : 18 : 20 : 22 : 23 : 25 : 26 : 28 : 29 : 30 : #include く stdio. h> #include く stdarg. h> enum TraceMode { error, infO c lass Trace { public: Trace() : Trace(const Char *name) : Trace(void) : Trace &operator() (const char *str) : Trace &operator() (TraceM0de traceMode, void doAssert(const char *file, int ド Trace &setTraceMode(unsigned int trace ode) : private: void indent(void); int assertSet(void) : int traceSet(void) : int errorSet(void) : inf0Set(void) : int ne, const Char *msg) : const char *fmt, static unsigned short level; / / インデント ( 字下げ ) static unsigned int traceMode; / / 出力許可ビット #define assert(trace. condition) { \ if (!(condition)) { \ レベル これは , 単一のスタティックな Trace のイ ンスタンスを , 文字列引数なしの Trace コ ンストラクタを使用して , Trace をインプ リメントするファイル内て、定義することて、 実現する。スタティックなインスタンスて、 あるのて、 , コンパイラは , main( ) を開始さ せる前にコンストラクタが呼び出されるよ うなコードを生成する。コンストラクタは , 環境変数から D 刊 BUGMODE を探して , ス タティックメンバ traceM0de を初期化す る。これは main( ) が開始する前に動作する のて、 , トレース出力は main ( ) が開始する前 に許可される。しかし , たとえ許可されて も , Trace 初期化のコンストラクタの前に コンパイラの開始コードに呼ばれるコンス トラクタは , トレース出力はもっていない て、あろう。もうひとつのコンストラクタは , Trace オプジェクトが前述のように定義さ れるとき呼ばれる , 通常のコンストラクタ て、ある。 関数呼び出しオペレータは 2 種類のものが 定義されている。ひとつは , パラメータと して一定の文字列をとり , トレースメッセ ージ出力をインプリメントするために用い られるものて、ある。もうひとつは , 第 1 パラ メータを列挙型 TraceMode として以下 , 出 カフォーマットを記述する文字列や , その 文字列の内容に従う可変個数のパラメータ という形をとるものて、ある。この関数の条 項て、ある Requires のなかて、詳細に示されて いるように , 呼び出す側は , 出力フォーマ ットを記述する文字列とともに , 正しい数 と型のパラメータを渡すことを保証しなけ ればならない doAssert オペレーションは assert マクロ によって呼び出される。 setTraceMode は , 環境変数 DEBUGMODE からセットさ れたデフォルト値の traceM0de を無効にす るために用いられる。 List2 にもこの使用例 が含まれている。 いくっかのプライベート操作が記述され C 十十のデバッギングクラス 39
五ロ はじめて学ぶ 0 プログラー ニンク Fig. 1 ストリームの概念 ファイル テパイスなど C プログラム テータの流れ ストリーム ( 双方向 ) ( バッフアを含む場合が多い ) ありません。よく使うへッダは限られてい 数については , ポインタの説明をした後て、 るのて、 , よく使うものから覚えていけばよ お話します。それて、は誌面の許す限り , 各 いて、しよう。 ヘッダごとに関数を紹介していきましよう。 6-4-1 ストリーム 入出力を理解するためには , まずストリ 6-3-1 ーム (stream) という概念を理解する必要が ( 1 ) stdio. h 標準ライプラリ関数の使い方 あります。ストリームとは , C プログラムと この stdio. h は , 今まて、何度も使ってきた ファイルとの間のデータ入出力の経路のこ 標準ライプラリ関数を使うためには , そ のて、 , いちばん馴染み深いと思います。 とて、す ( Fig. 1 参照 ) 。ここて、は , ディスク装 stdio. h は STanDard I/O Header の略て、 , の関数を宣言しているヘッダを取り込まな 置とか , 周辺機器などと結合しているデー 標準入出力へッダのことて、す。このヘッダ くてはいけません。書式は , タの発信元 (source) と受信先 (destination) のなかには , 入出力に関する関数のプロト #include <header> のことを指します。ストリームは , 各種フ タイプ宣言などが数多くしてあり , 標準ラ あるいは , ァイルやデバイスとはオープンすることに イプラリの約 3 分の 1 を占めています。 #include "header" よって結びつけられ , その結びつきはクロ て、す。ヘッダ名を < > て、囲むと , 環境変数 ーズすることによって解消されます。プロ て、指定されたディレクトリが検索されます。 ヘッダ名を ' ' て、囲むと , まずカレントディ レクトリが検索されます。もし指定された ファイルがないときは , 環境変数て、指定さ れたディレクトリが検索されます。ふつう は < > て、ヘッダを指定します。 ヘッダは , 何回取り込んて、もかまいませ ん。また , ヘッダを取り込むためには , す べての外部宣言や定義の外側て、 , かっ , へ ッダて、宣言するものより前にある必要があ ります。 List 1 ①②③④⑤⑥⑦⑧⑨ int fprintf ( FI し E *stream, const char *format, int fscanf( FI し E *stream, const char *format, int fputc( int c, FI し E *stream ) : int putc( int c, FI し E *stream ) : char *fgets ( char *s, int n, FILE *stream ) : int fputs( const char *s, FILE *stream ) : VOid srand( unsigned int seed ) : double atan2( double Y, double x ) : double pow( double x, double Y ) : CTbl.2] ストリーム ストリーム stdin 標準入力 標準出力 stdout 標準工ラー出力 stderr 標準プリンタ出力 std p 「 n 標準補助出力 stdaux ただし , stdprn, stdaux は UNIX にはない。 誌面の制約もあり , こてすべての関数 を説明することはてきないのて , よく使う 関数に限って説明をしていきます。また , ポインタについての説明をまだしていない のて , ポインタを用いた標準ライプラリ関 通常割当テパイス 呼び名 キーボード 画面 画面など プリンタ RS ー 232C など はじめて学ぶ C プログラミング 121
特集 AN 0 の現状 Fig. 4 ANSI C の現状と拡張の概念 ・現状 例 ) char * strstr(const char * s1, const char * (2) ; 重する ( IS02022 符号拡張系のような シフトエンコーディングの世界もカ ーする ) ( 3 ) 以上をふまえたうえで , 「 1 文字」を「 1 単 位」として扱えるよう wchar t のライ プラリを拡張する ( 4 ) 仕様の決定 , 対策の検討などにあたって は , その理由を示す理由書を残し , 3. アプローチ 本体と対で公開する するという保証はしている。ただし , 「日本 ともバイトとしてのピットパターンは透過 以上のレベルで ANSI C の現状は , 少なく AN SI C の現状 ることを目標としているのである。 検索 , 切り張りなどを文字の世界で再現す いう論理的な概念での入出力 , パターンの われは , 悩まされることなく自由に文字と イトなのかで悩まされてしまうのだ。われ 2 バイトからなっているのか , あるいは 1 バ の世界との変換が行われると , 必すここで , 界でしかないのである。要するに , バイ ているが , 入口と出口はやはりバイトの世 wchar t との間に変換ライプラリを導入し た。しかし現状の ANSI C では , char と えるような wchar という概念が持ち込まれ C のレベルでいえは , 1 文字を 1 単位として扱 ところが , マルチバイトの登場で , ANSI やっていた ンをサーチしたりすることをライプラリで 力して , それを切ったり張ったり , パター た。すなわち , 何ら問題なく外部から入出 のときは , バイトの世界も文字の世界だっ 世界としてあるが , 1 バイト = 1 文字の世界 る拡張範囲である。バイトの世界と文字の C で , グレー地の二重枠が私たちの行ってい Fig. 3 の赤地一重枠の範囲が現状の ANSI ーチをしているか Fig. 3 に示した。 以上の方針に従って , どのようなアプロ s 1 s 2 ↓ - x - x x bl b2 ・・ : bl b2 日 ・拡張の概念 例 ) wchar t * wcswcs(count wchar t * sl,const wchar t * (2) ; ↓ 日 0 bl 0 x bl b2 ・・ s 2 : 0 x 0 x : bl b2 日 語」の「日」が 2 バイトからなるから , 2 バイト を文字単位として認識しようということで は決してない。したがって , シフトシーケ ンスの解釈・管理 , 文字としての論理単位 の認識はアプリケーションプログラマの責 任ということなる。 たとえは strstr 関数の例を挙げてみよ う。これは , 第 1 アーギュメントの文字列の なかに , 第 2 アーギュ . メントの文字列を構成 するバイトの集合 ( サプセットでもよい ) か らなる , 部分列を探す関数である。 たとえば , Fig. 4 に示したような文字列が あったとして , s2 が「日」という文字列を指定 したとしよう。これは分解すると , 2 バイト の系ならば , bl と b2 というバイトからなって いる。ところが , 文字列全体の構成を見る と , ここに示した blblbl という部分列も「日」 を構成する最初のバイトに一致することに なる。したがって , このしのみからなる部分 文字列も検索対象になってしまうのである。 つまり , 「日」という単位とは認識せす , bl と b2 からなる列としてしかみなされていないの だ。これでは「日」という文字 ( 論理単位 ) を 探したいという要件は成立しないことにな る。 そこで , 文字を文字としてきちんと認識 する strstr に対応の関数 wcswcs を導入し た。この関数だと , Fig4 の例の場合は wchar ストリングとみなしてくれる。たとえば , こでの例だと 2 バイトで 1 単位を構成して いるとして , 「日」と指定したら , 今度はち ゃんとした単位の 1 ユニットだと認識する。 このことにより strstr では bl のような個所 でひっかかっていたものが , 正しく「日」と いうところにサーチボジションに行く。拡 張の概念とは , こういう単位で扱えるとい うことである。 拡張の概念 特集 ANSI C の現状 59
なお , PC ー 9800 シリーズては , 半角の 2 バ イトコード ( 0X8540 ~ 0X869e ) も入力てきま すが , 半角文字の画面制御は , 今回取り上 げません。 0X853F ~ 0x85DD のコード (ASC118t•ットコードに変換可能 ) に関して は , 1 バイトコードへ変換します。 また , 各種制御コードもこの関数の中て 処理します。 この関数の中ては , ファンクションキー や囮などのキーをエラーとしてはじいて います。これらのキーを入力したい場合に は , それぞれのキーの処理を switc れ ~ case 文に挿入追加してください なお , ファンクションキーは , ESC コー ドて始まっているのて ( PC ー 9800 シリーズの 場合 ) , あらかじめキー割りつけを変更して おいたほうが , 処理が簡単てす。 文字列の任篇の文字種を知る関係 (test-stræ数 ) 任意にカーソルを動かして文字列を入力 するためには , 文字列バッフアのカーソル 位置に対応した文字の種類 ( 1 バイトコード / 2 バイトコード ) を知る必要があります。と くに , ANK と漢字混在て入力するために は , ぜひともこの関数が必要になります。 2 バイトコードは , 2 バイトそろってはじ めて意味を成すものてす。たとえば , 2 バイ トコードの文字を 1 バイトコードて、上書きす ると , どちらかが宙に浮いた形となってし まいます ( Fig. 4 ) 。てすから , 上書きする前 にカーソル位置の文字が , 2 バイトコードか 否かを知り , 必要に応じて片方の 1 バイトを 空白てクリアするなどの処理が必要になっ てくるのてす。 ところて , シフト JIS コードは第 1 バイト だけを見ると , 漢字か否かを判別てきます が , 第 2 バイトは , ASCII コードとダブッて いる (Fig. 4 ) ため , 第 2 バイトだけを見て も , その文字が漢字か否かを判別すること はてきません。さらに悪いことに , 第 1 バイ トともダブっている ( 例いョ〃 = 0X8388 ) の て , 任意の 1 バイトを見ただけては , その 1 バイトが ANK なのか , 漢字の第 1 バイトな のかそれとも第 2 バイトなのかを判断するこ 応用 C 言語 115 五ロ - 三ロ 用 応 get_strln 1 : #define B し ANK 2 : 3 : 4 : / * get-strln 5 : 6 : get-strln(unsigned char *buf, int cnt) int 8 : Char *ptr: 9 : int ptr=buf 十 cnt-l : / * 最後の文字をポイント * / for ( : (*ptr==B し ANK) & & (buf く =ptr) : ) 13 : ptr-- 14 : rc=ptr-buf + 1 : return (rc) : List 4 / * SPACE * / put_str 1 : #include く stdio. h 〉 2 : 3 : 4 : / * put-str 5 : put-str(int x, int y, char *buf, int count) 6 : VOid csr-posit (). y) : 8 : printf("X. *s", count, buf) : 9 : return : List 5 0 1 : # i ncl ud e く std i 0. h> 2 : #include く dos. h> 3 : #include く string. h> 4 : 5 : #define ORG 6 : #define REV 7 : #define UNDER 8 : #define B い NK 9 : 10 : #define RED 11 : #define BLUE 12 : #define PURPLE 13 : #define GREEN 14 : #define YELLOW 15 : #define SKY 16 : #define WHITE 18 : #define KHEADI 19 : #define KHEAD2 20 : #define KHEAD3 21 : #define KHEAD4 22 : #define HANKJI 23 : #define HANKJ2 24 : #define HANKJ3 25 : #define HANKJ4 26 : #define CONST 1 27 : #define CONST2 28 : #define CONST3 29 : 30 : #define B し ANK 31 : #define FWD 32 : #define BACK 33 : #define INS 34 : #define DE し 35 : #define ESC 36 : #define CR 37 : #define BE しし 38 : #define HOME_CLR 39 : 40 : #define ÅNK 41 : #define KANJI 1 42 : #define KANJI 2 43 : List 6 ” 22 ” ” 23 " 0X80 0x9f 0xe0 0xfe 0x3f 0xde 0x7f 0x9e 0xlf 0X20 0X02 0x0c 0X08 0X12 0x7f 0xlb 0x0d 0X07 0x1e 0 1 2 / * 漢字第 1 バイト境界 ( 先頭 ) * / / * 漢字第 1 パイト境界 ( 最終 ) * / / * 漢字第 1 パイト境界 ( 先頭 ) * / / * 漢字第 1 バイト境界 ( 最終 ) * / / * 半角漢字第 2 パイト境界 ( 先頭 ) * / / * 半角漢字第 2 パイト境界 ( 最終 ) * / / * 半角漢字第 2 パイト境界 ( 先頭 ) / * 半角漢字第 2 バイト境界 ( 最終 ) * / / * 半角漢字定数 for HÅNKJ3 * / / * 半角漢字定数 for HANKJ4 * / / * 半角漢字定数 for その他 * / / * SPACE * / / * Return * / / * BUZZER Ascii control COde / * HOME_C し R * / / * 漢字第 1 バイト * / / * 漢字第 2 パイト * /