mo ンはいらない。しかし , 実際には次のよう にセミコロンを打っても現実のコンパイラ て、はエラーにならない int foo(int n) decla rator-list int foo(int n) 0 が if (n < 2 ) return 1 ・ return fOO(n declaration-specifiers : storage—class—specifier declaration—specifiers type-specifier declaration- specifiers 1 ) * n ; if (n < 2 ) 「 eturn 1 ・ retu 「 n f00 (n なぜなのか ? 歴史的事情からなのか ? これは , 文法的には誤りのはずて、ある。 単にコンパイラ作成上の都合て , ェラーチ 0 が なぜならば , 最後のセミコロンは関数定義 ェックを省いているだけなのか ? type-qualifier declaration- の一部て、はありえず , 関数とは独立した存 筆者は最初 , 関数定義の最後にセミコロ specifiers 在てある。ソースのトップレベルに存在し ンを打っというスタイルを友人て、第一級の 0 が ハッカー ( よい意味ての ) T 氏のソースを見て 得るのは外部宣言の並びて、あり , 外部宣・ だんだん複雑になってきて恐縮だが , の定義は次のように読める。 知った。そのときどうしてそこにセミコロ とは関数定義か , ( そのほかの ) 宣言かのい ンを打ってよいのかが理解て、きなかったが , ずれかて、ある。これは次の構文則によって 言とは : ・宣言指定子の後に省略可能な初期 いずれにせよ , これは構文定義と現実のコ 定められている (ANSI 3.7 ) 。 子リストを並べたものである ンパイラが受理する文法仕様が異なってい translation-unit 宣言指定子とは . る典型的な例のひとって、ある。 externa ト declaration ・記憶クラス指定子の後に省略可能な宣 translation-unit externa ト C におけるカンマの役割 言指定子を並べたものかまたは declaration ・型指定子の後に省略可能な宣言指定子 さて次にカンマを取り上げる。カンマの を並べたものかまたは externa ト declaration : 使われ方は , 幸いにもセミコロンほど複雑 ・型修飾子の後に省略可能な宣言指定子 function-definition て、はない。またそれぞれ , 直観て十分に把 を並べたものである declaration 宣言指定子を , もう少しわかりやすく表現 握可能な使われ方て、ある。 これは次のように読む。 すればこうなる。 翻訳単位 ( ファイルと考えてよい ) は . : : 絣カンマ演算子 宣言指定子とは ・外部宣言であるかまたは カンマは , 実は 2 項演算子の一種てある。 ・記憶クラス指定子か ・翻訳単位の後ろに外部宣言が並んだも ・型指定子か カンマ演算子は , 大部分の 2 項演算子とは異 のである ・型修飾子かを少なくとも , ひとつ以 なり , 評価の順序を規定しているというの 上並べたものである が特徴て、ある。すなわち , まず左側の部分 外部宣言とは : こて注意すべきは「空」の宣言は存在し 式を評価し , それが完了したのちに右側の ・関数定義であるかまたは ないということて、ある。少なくとも , 記憶 部分式を評価することが定まっている。 ANSI 言である このように定められているため , 関数定 クラス指定子か , 型指定子か , 型修飾子か 流の表現をすれば , 左側の部分式を評価し のいずれかひとつを指定することが構文上 義の最後に付加したセミコロンは , もし許 た後 , 右側の部分式を評価するまて、の間に されるとするならば , それは宣言として解 は要求されている。いい換えればセミコロ シーケンスポイントが存在する。 ンだけの宣言は構文上は許されないはずだ カンマ演算子はマクロの内部などて、用い 釈されているはずて、ある。 て、は , 宣言 (declaration) とはどのような ということてある。ところが , 実際には筆 られることが多いが , 普通はあまり表だっ 者の知るかぎりのコンパイラにおいては工 て使われることはない。しいてよく使われ 構文になっているかというと , 次のように ラーにならない。次のような極端な例をコ るケースをあげれば for 文においてだろう。 規定されている ( ANSI 3.5 ) 。 for て、は , 初期化 , 繰り返し条件 , 更新のた ンパイルしても , 手元のコンパイラのすへ declaration ・ めの三つの式を指定してループをコントロ てがエラーを出さずに通してしまった。 declaration—specifiers init— 0 が 1 ) * n ; 宣 ANSI C ー more 123
て見ていることになります。 接続番号からネットワーク番号とノード ノードアドレスはイーサネットのネット アドレスを得ます。ネットワーク番号はひ ワークポード固有のネットワークアドレス とつのセグメント内 ( インタネットワーク ) て、 , 世界にひとっしかないアドレスて、す。 が表される番号のことて、 , セグメントはす べて同じ番号となります。 したがって , ひとつのセグメント内にあ るサーバのネットワーク番号はすべて同じ NetWare て、はシェルにより ,MS-DOS の て、す。 たとえば , NetWare が標準て、サポートし ファイル , ディレクトリとまったく同じよ ている slist. exe などて、ネットワーク番号が異 うに扱うこともて、きますが , NetWare とし ての特有のサービスを受ける場合にはファ なるサーバが表示されたら , それはセグメ ント外のネットワークをプリッジを経山し イルおよびディレクトリサービスの API を使 ネットワークへの接続状況を表示する (netwami. c) 接続番号の取得 GetConnectionNumber 関数 サーバとの接続番号を取得 ファイルサーバの接続番号を取得します。 接続情報の取得 ファイルおよび ティレクトリサービス GetConnectionlnformation 関数 ーー接続情報を取得 指定した接続ヘログインしているオプジ ェクトの情報を取得します。この関数て、は 以下の情報を得ることがて、きます。 ・オプジェクト名 ・オプジェクトタイプ ・オプジェクトの ID ・ログイン時間 オプジェクトのタイプは finger. c て、説明し こて、は , オプジェ た値が出力されます。 クトは自分自身なのて、オプジェクト名は自 分のログイン名 , オプジェクトのタイプは ューザとなります。 現在接続中のサーバ名の取得 lSt 1 : #include く stdio. h 〉 2 : #inc lude く process. h> 3 : #include く nit. h> 4 : #include く niterror. h> 5 : 6 : void 7 : main(int argc, char *argv[]) 9 : WORD connectionNumber; WORD objectType : long 0bjectID; 12 : IoginTime[7] : BYTB user[48] : 13 : Char WORD connect i on ー D : serverNameC48] : Char network [ 4 ] , node [ 6 ] : 16 : Char 17 : WORD socket; int completionCode; / * 接続番号を取得 * / 20 : 21 : connect i onNumber ニ GetConnec t i onN umber ( ) : if (connectionNumber ー ニ 0 ) { 22 : printf ("NetWare shell not IoadedYn") : 23 : exit(l); 24 : 25 : 26 : &objectType. &objectID, loginTime) : / * 接続情報を取得 * / 27 : 28 : completionCode = GetConnect ionl nformation(connectionNumber, user, if ( compIetionCode ! = SUCCESSFUL ) { 29 : fprintf (stderr, "GetConnectionInfomat ion 接続情報を得られません 30 : exit(l); 32 : Yn 工ラーコード : %02XYn", compIetionCode ) : 33 : / * 現在接続しているサーバーの接続 ID を取得 * / 34 : 35 : connectionID = GetDefaultConnectionID() : 36 : / * ファイルサーバー名を取得 * / GetFi leServerName(connectionID, serverName) ; 38 : 39 : / * ログイン情報の表示 * / 40 : printf(" ユーザ %s はサーバー %s に " user, serverName ) : printf(" %d-%d-%d %d:%d にログインしました \ n " , 42 : IoginTimeC0], IoginTime[I), IoginTime[2], 43 : loginTime[3], loginTime[4) ) : printf(" ワークステーションの接続は %d , 接続番号は %d です \ n " 44 : 45 : / * インターネットワーク情報の取得 * / [ ーー connectionl D, connectionNumber) : 46 : compIetionCode = GetInternetAddress(connectionNumber, network, node, &socket) : 48 : i f ( compIetionCode ! ニ SUCCESSFUL ) { fprintf (stderr, "GetInternetAddress インターネットワーク情報を 49 : exit(l); 50 : 得られません \ n 工ラーコード : %02XYn", completionCode ) : 52 : / * ネットワークアドレス等の表示 * / 53 : printf( " ソケット番号 : % 04X 54 : ”ノードアドレス : % 081X % 04X 55 : " ネットワーク : % 08 Ⅸ Yn", 56 : I ntSwap (socket) , IntSwap(*((int * ) (node + 4 ) ) ) , LongSwap(* ( long *)node) ) , LongSwap(* ( long *)network))) : 59 : 60 : } ス レ ア ノ 番 * ク号ド トト目 * ワトコ * クク時 * / 名トツ一 号工エン名 * 一ッケラ 番ジジイザバネソ工 続ププグ一続一 * * * 接オオロュ接サ / / / GetDefauItConnectionID 関数 接続中のサーバへの接続を取得 この接続 ID は接続番号とは異なり , 今自 分が接続しているいくつかのサーバのうち , 実際にサービスを求めているサーバが何番 目かを示すものて、す。接続番号とは , サー バに接続している端末のうち , サーバにサ ービスを求めてきた端末が何番目て、あるか を示すものて、す。 GetFiIeServerName 関数 サー / ヾ名を取得 接 *%ID によりサーバ名を得ます。 ネッワークアドレスの取得 GetInternetAddress 関数 ネットワーク番号 , ノードアドレスを取得 64 C MAGAZINE 1991 8
変更は次の 3 点てす。 ① intdos, int86 関数などて参照する union REGS のメンバに , cflag が追加されまし た。今まては flags の最下位ビットを検査 してエラーの有無を判定する必要があり ましたが , この結果 , 少しだけ MS ー C と共 存しやすくなります ( 本来はキャリーしか 参照てきない MS ー C の仕様のほうがおかし いのてすが ) 。 ②ヘッダく fcntl. h > にマクロ O BINARY が 追加されました。これも MS-C との互換性 を考えてのものてしよう。しかし , マク ロ 0 TEXT は定義されておらず , read/ write 関数などの仕様 ( バイナリモードのみ ) は変更されていません。 ③ヘッダのプロトタイプ宣言に const が使用 されています。主としてエラーチェック の強化に役立ってしよう。 コンバイル時間と オプジェクトサイズ コンパイル時間とオプジェクトサイズの 比較を TabIe 1 に示します。 Ver. 3.20 に比 べコンパイル時間が少し遅くなり , オプジ ェクトサイズも少しだけ大きくなっていま す。オプジェクトサイズが大きくなってい るのはスタートアップルーチンが少しだけ 大きくなり (TabIe 2 参照 ) , ライプラリ関数 も一部 ( 前述の intdos 関数など ) 機能アップさ れたためてす。アセンプリリストを生成し TabIe 3 DHRYSTONE べンチマーク コンパイラ コンバイルスイッチ /J /Zp /Ox MS-C 6.0 /J /Zp /Gs MS-C 6.0 /J /Zp /qc MS-C 6.0 Turbo C 十十 1 .0 -K -G -0 Turbo C 十十 1 .0 LSI C -86 3.3 -cu -0 -lintlib LSI C -86 3.3 -cu ー 0 -lintlib -ltinymain. Obj LSI C -86 3 .3 -cu -lintlib LSI C ー 86 3.2 LSI C -86 3.2 -ltinymain. Obj LSI C -86 3.2 -cu -q 注 ) 実行時間は DH 日 YSTONE コマンド ( DH 日 Y•EXE ) 自体の実行時間 40 C MAGAZINE 1991 8 TabIe 2 void main( void ) { } の EXE ファイルバイト数 コンバイラ EXE ファイルバイト数 2887 バイト MS-C 6.0 1328 バイト Turbo C 十十 1 .0 5308 バイト LSI C ー 86 3.3 3386 バイト LSI C ー 86 3.3 4992 バイト LSI C -86 3.2 3072 バイト LSI C ー 86 3.2 構造体を返す関数の返り値にメンバ演算子ツ″を使用できない 備 考 -ltinymain. Obj -ltinymain. Obj LiSt 1 : struct sa { 2 : long に 3 : i nt i : 5 : 6 : long lvar; 7 : 8 : struct sa func( void ) : 9 : 10 : void func2( void ) = func().l : 13 : } / * 左辺値が必要 (. ) * / lvar 整数の格上げルール List 1 : # i ncl ud e く std i 0. h > 2 : 3 : unsigned char ucl 4 : unsigned char uc2 5 : 6 : i nt ma i n ( vo i d ) 8 : if ( ucl ー uc2 く 0 ) / * unsigned char での比較となる * / 9 : printf( "successYn" ) : return( 0 ) : 実行時間 6.27 秒 8.15 秒 12.1 1 秒 8.53 秒 8.74 秒 7.60 秒 7.90 秒 7.98 秒 7.87 秒 7.97 秒 8.08 秒 EXE ファイル バイト数 8835 バイト 8979 バイト 9555 バイト 10048 バイト 10048 バイト 9918 バイト 8334 バイト 10014 バイト 9548 バイト 7982 バイト 9644 バイト dhrystones /second 8333 6250 4166 6250 6250 7 142 7 142 7 142 7142 7142 6250 備考 最大限の最適化 テフォルトの最適化 クイックコンバイル 最適化 テフォルトの最適化 最適化あり 最適化あり 最適化なし 最適化あり 最適化あり 最適化なし
正常終了の場合は 0 を返す [ 機能 ] ウインドウ winno を開いて , 各フィ データを入力する keidraw2( ) 関数 ( CM910804 , List 4 ) ールドに menurd2( ) 関数 ( CM910806 , List 6 ) メニューファイルから情報を得て , 各画 面作成ルーチンを起動 [ 書式 ] #include #include "fkey. h" ” menurd. h ” 構造体 mn て、指定するポックスカーソルメニ ュー画面を表示色 rcol 。 r て、表示し , 選択した メニュー番号を返す seldraw2 ( ) 関数 ( CM910808 , 付録ティスク seldraw2. c) メニューファイルから情報を得て , 各画 面作成ルーチンを起動 - ー一文字列から情報を取り出し罫線を表示 [ 書式 ] #include #include "fkey. h" menurd. h ” menurd2(fiIename,cmd,arg) char *filename char *cmd ; char *arg , [ 引数 ] 起動する。メニューによる選択の結果が * クスカーソルなどの表示を行うルーチンを を得て , 罫線 , ファンクションキー , ポッ メニューデータファイル filename から情報 [ 機能 ] 正常終了の場合は 0 を返す [ 戻り値 ] *arg ←引数 * cmd ←起動するコマンド *filename ←メニューデータファイル名 ボックスカーソルによるメニュー選択 ( CM910807 , List 7 ) selbar2( ) 関数 cmd, * arg に代入される [ 書式 ] #include #include "fkey. h ” ” menurd. h ” #ifdef PROTOTYPE "cboxprot. h" #include #endif keidraw2(moji) char *moji : [ 引数 ] *moji ←画面情報の入っている文字列 [ 戻り値 ] 正常終了の場合は 0 を返す [ 機能 ] 文字列 moji から情報を得て罫線を表示する keydraw2 ( ) 関数 ( CM910805 , List 5 ) ー一文字列から情報を取り出し , ョンキーを表示 ファンクシ [ 引数 ] mnC] [ 書式 ] #include #include "fkey. h" 'menurd. h ” keydraw2(moji) char *moji ; [ 引数 ] *moji ←画面情報の入っている文字列 ←メニュー番号 , メニューにおけ 号からなる構造体 および最初に反転表示する行番 の XY 座標 , 表示データの格納行 罫線の表示属性 , 表示する領域 る選択肢の数 , 各行の表示属性 , C010r ←表示色 rcolor ←反転表示色 linenum ←行数 [ 戻り値 ] 正常終了の場合は 0 を返す [ 機能 ] 文字列 moji から情報を得て , ンキーを表示する 110 C MAGAZINE 1991 8 initl ura [ 戻り値 ] ←最初の反転行 ←表 / 裏画面 ファンクショ 選択したメニュー番号を返す [ 機能 ] seldraw2(moji,fp,cmd,arg) char * moji ; FILE *fp ; cha 「 *cmd char *arg [ 引数 ] *moji ←画面情報の入っている文字列 * fp ←メニューデータファイル名のポ インタ * cmd ←起動するコマンド 引数 * arg ← [ 戻り値 ] 正常終了の場合は 0 を返す [ 機能 ] メニューデータファイル fp および文字列 moji グラムにリンクする必要がある。 のライプラリに追加し , 最後にメインプロ コンパイル後オプジェクトファイルを読者 ルスイッチをそれぞれ以下のように設定し , ラムは分割コンパイル用なのて、 , コンパイ パイル可能なものてある。なお , 各プログ 本プログラムは , MS-CVer. 5.1 て、コン コンバイル 果が *cmd, * arg に代入される ーの表示を行う。メニューによる選択の結 から情報を得て , ポックスカーソルメニュ 同様にコンパイルスイッチを設定するとと MS-C て、コンパイルする場合には , 従来と 【 MS-C 】
箱 語具 応の [ 書式 ] inpdraw2( ) 関数 ( CM910802 , List 2 ) ーー関数文字列からの情報で入力画面を作成 [ 書式 ] *SELECT *INPUT -COL : COLORI ; COLOR2 ; SCREEN -COL ; WINNUM ・ COLORI ; 8LOR2 : STARTLINE -FIELD ; NUM;WINNUM;X;Y;DEFAULT; [ 書式 ] -FIELD ; NUMBER ; X : Y ・ CONTENT LEN ; STYLE COMMAND ・ ARG -END 各パラメータの意味は Fig. 8 とおりて、あ -END まず行の先頭に、、 \ 〃マークを置く。次に 、、 S 〃が続くとポックスカーソルコマンドと ・ FUNCTIONKEY 認識される。そして次の行から詳細な指定 [ 機能 ] を行う。この指定をするコマンドには ファンクションキーの表示を変更する inpdraw2(moji,fprcmd,arg) マークを先頭につける。指定の仕方は Fig. 11 [ 書式 ] *FUNCTIONKEY : FIAF2 年 3 年 4 char *moji ; のとおりて、ある。 F5 年 6 年 7 年 8 年 9 乍 10 ; COLOR , FILE * fp : 付録ディスク MM ℃にソースを掲げる。 char *cmd 今回は , サプウインドウとオプションの SCREEN 指定を対応させていない。サプウインドウの char *arg まず行の先頭に〃マークを置く。次に [ 引数 ] 表示方法には , 単に表示するもの , ボック 、、 F 〃が続くとファンクションキー表示変更 *moji ←画面情報の入っている文字列 スカーソルによる選択可能なもの , そして コマンドと認識される。これも罫線コマン * fp ←メニューデータファイルのファ 直接入力能なものの 3 種類を用意している。 ドと同様に最初の 1 文字て、認識される。 イルポインタ 次回はこれらのサプウインドウを有機的に 次に各パラメータをセミコロン、、げて、 [ 戻り値 ] 組み合わせて , より複雑な指定が可能なも 区切りながら指定する。各パラメータの意 正常終了の場合は 0 を返す のに挑戦するつもりて、ある。 味は Fig. 9 のとおりて、ある。 [ 機能 ] ・ WINDOW 道具箱に追加された関数 文字列 m 。 ji から情報を得て入力画面を構成 [ 機能 ] する サプウインドウを表示する [ 書式 ] bkdraw2( ) 関数 inpmenu( ) 関数 *WINDOW ( CM910801 , List 1 ) ( CM910803 , List 3 ) -WINCREATE ・ WINNUM ; XI ; YI : X2 ; Y2 ; ー一文字列から情報を取り出し背景画面を作 ウインドウを開いて各フィールドに入力 -COL ・ WINNUM ・ COLORI ・ 8LOR2 : 成 -FIELD : WINNUM : X ; Y ; ØNTENT COMMAND ; ARG -END まず行の先頭に〃マークを置く。次に ヾ W クが続くとウインドウコマンドと認識さ bkdraw2(moji) れる。そして次の行から詳細な指定を行う。 この指定をするコマンドにはノマークを char *moji ; [ 引数 ] 先頭につける。指定の仕方は前ページの Fig. *moji ←画面情報の入っている文字列 10 のとおりて、ある。 [ 戻り値 ] ・ SELECT 正常終了の場合は 0 を返す [ 機能 ] [ 引数 ] [ 機能 ] ポックスカーソル選択画面を表示する。あ winno ←ウインドウ番号 文字列 m 。 ji から情報を得て背景画面を構成 らかじめ指定した実行ファイルに分岐を行 [ 戻り値 ] する フ "fkey. h" "pldwn. h" ” menurd. h ” ” window. h ” winvar. h ” #include #include #include #include #include [ 書式 ] ” menu 「 d. h ” ” window. h ” ” winvar. h ” "pldwn. h" "fkey2. h" "input. h" inputvar. h ” #include #include #include #include #include #include #include [ 書式 ] "fkey. h" ” menu 「 d. h ” #include #include inpmenu(winno) int winno ; 応用 c 言語 109
アルゴリズムアータ構造入門 のクイーンによる利き筋を反映させます ( 76 ~ 78 行目 ) 。 もし , N 個のクイーンを置いてしまった ら , 解が見つかったことになるのて、 , SUCCE SS を返してリターンしてしまいます ( 81 , 82 行目 ) 。 そうて、なければ try ( a 十 1 ) を呼び出して a 十 33 : } 1 行目以降のすべてのクイーンを配置させ , もし成功したら SUCCESS を返します ( 85 , 86 行目 ) 。 try (a 十 1) が失敗なら , 置いたクイ ーンを取り除きます ( 89 ~ 92 行目 ) 。そして 67 行目の for 文て、次の場所にトライします。 もし , (), 0 ) ~ (), N ー 1) のすべての場所 Fig. 8 1 : 3 : 4 : 5 : 6 : 7 : 8 : 9 : 12 : 13 : 14 : 8 クイーンの解法 ( すべての解を見つける ) void try-all (a) for ( 場所 (), の , (a,l) ・・・・ ( a , 7 ) について繰り返す ) { i f ( この場所は、他のクイーンの利き筋になっていない ) この場所にクイーンを置く / * すべてのクイーンが置けた 盤面を表示する } e I se { try-all(a + 1); クイーンを盤から取り除く N クイーン ( すべての解を表示する ) List 2 2 : 4 : 5 : 7 : 8 : 9 : 12 : 13 : 14 : 19 : 20 : 22 : 23 : 24 : 25 : 26 : 28 : 29 : 30 : 32 : 1 : / * a 行目以降すべての行にクイーンを置いてみる ( すべてのパターンを表示する ) * / void try-all(int a) i nt b ; / * 左から右に向かって順番にクイーンが置けるかどうかを調べる * / / * a 行目の b 番目に置けるかどうか調べる * / for (b ニ 0 : b く N ; b + + ) { ニ NOT_FREE; down[a ー b + (N-I)] upCa + b] ニ NOT-FREE; ニ NOT_PREE; col [b] pos[a) / * 置くことができた。場所を記録して、利き筋をセットする * / down[a ー b + (N-I)] = = FREE) { = FREE & & 叩 [a + b] ニ FREE & & if (colCb] / * N 個のクイーンをすべて置くことができれば成功である * / if (a 十 1 > ニ N) print_queens() : try-all(a + 1); e ー se / * クイーンを取り除く = FREE; pos[a] col Cb] up[a + b] = FREE; down[a ー b + (N-I)] ニ FREE; について失敗した場合 , a 行目にはクイーン を置く場所がなかったことになり FAIL を返 します ( 99 行目 ) 。 最後に , 関数 main がメインルーチンて、 す。ま iinit board を呼び出して初期化を行 い , 次に try ( 0 ) を呼び出して解を探します。 もし , 成功したら print_queens によって盤の 内容を出力します。もし失敗したら , 解が 見つからなかったというメッセージを表示 します。 すべての解を探索する List 1 は , 解をひとつだけ見つけるプログ ラムて、した。しかし , すべての解を探し出 したいというケースもあります。また , 最 適の解を見つけたいというケースもありま す。この場合には , すべての解を探索しな がら , 最適な解を記録しておく , とが必要になります。バックトラック法を 使えば , すべての解を調べ上げることは容 易て、す。 すべての解を調べ上げるための疑似コー ディングを Fig. 8 に示します。これを Fig. 4 ( ひとつだけ解を求めるバージョン ) と比べ ると , すべてのクイーンを配置することに 成功したとき , その場て、盤面を表示して , クイーンを取り除いて別の位置をトライす るという点が違っています。すべての解を 求める Fig. 8 のほうが Fig. 4 よりもすっきり としています。 すべての解を表示する関数 try a11 を List 2 に示します。この関数は , Fig. 8 の疑似コー ディングをそのまま C に書き換えたものて、 す。また , プログラムの基本的な流れは List 1 の関数 try と同じものてす oinit board によ って初期化を行った後 , この関数を try all ( 0 ) として呼び出せば , すべての解を表示し てから戻ってきます。ちなみに , 8 クイーン の解は全部て 92 個あります ( 対称性を考慮す れば , 実際の解の数はこれより少なくなり ます ) 。 アルゴリズムとデータ構造入門 87
ールてきる。それぞれの部分てどうしても 複数の式を指定したい場合などにカンマ演 算子を用いると可能になる。 C, C for(i 初に比較例としてあげた if 文て、は , プレース うとおもしろいコーディングがてきる。最 ところて , 式とカンマ演算子をうまく使 て、 , あまり気にする必要はないかもしれな ため , ほとんどコンパイルエラーになるの と書くと , 型が予期してたものとは異なる ただし C て、 , a Ci] [j] のつもりて、 a Ci,j] Cj] は Pascal て、は互換性のある記述て、ある ) 。 いるものと解釈されるため , a [i, j] と a Ci] [j] と書くべきところを省略して記述して ( なお Pascal て、は , a Ci,j] は , 本来は a Ci] グラマはうつかりしてしまうかもしれない う 0FORTRAN や Pascal などに慣れたプロ 算子て、あり , a [j] と同じ意味を持ってしま j] とは書かない。後者のカンマはカンマ演 セスするのに a [i] [j] などと記述し , a [i, また , C ては 2 次元以上の多重配列をアク やみに使わないほうがよい べきところをつい見落としがちなのて、 , む かが読みにくく , また後々 , 修正時に直す の変数がループをコントロールしているの だが , カンマ演算子を多用した for は , ど f0 「 (j = (i = 0 ) 十 n ; i く 10 ; 十十 i 十 --j) { カンマ演算子を使って書く。 演算子は使わなくてよい。しかし , 普通は とえば上の例は次のように書けば , カンマ ンマ演算子を使わずに済むことが多い。た る。トリッキーなコーディングをすればカ は i をインクリメントし , j をデクリメントす 代入してループを開始し , 各ループの後に 上の例は , 最初に i を 0 にし , j には n の値を を使うことなく , 次のようにコーディング b, b 構文的に if は次のように定義される。 a if ( 式 ) 文 1 celse 文 2 ] ンマ演算子が評価順序を規定しているため どの副作用がある操作を行う場合にも , カ ことがて、きないからて、ある。また , 代入な い方だが ) がきていてはカンマ演算子て、結ぶ うな場合に限られる。本当の文 ( 不正確ない の内側にきているのがすべて式文て、あるよ このようなことが可能なのは , プレース ことになり , プレースは不要になる。 ことて、全体としてひとつの文とみなされる そこて、最後にひとつだけセミコロンを打つ 合には結局全体としてひとつの式となる。 カンマ演算子て、結ぶことが可能て、 , その場 らは本来「代入式」て、あることを考慮すると , してみなすのが普通て、ある。しかし , これ し , プレースて囲んて、全体をひとつの文と れぞれにセミコロンを打って三つの「文」と め , 三つの別々の代入を行う場合には , そ ひとつの「文」てなければならない。このた 0 以外の場合に実行すべき「文 1 」は , 構文上 ることを意味する。条件を表わす式の値が こて、は , 、、 [ ] 〃の内側は省略可能て、あ 用例 2 実引数の区切りのカンマ に安心して用いることがて、きる。 カンマの中て、もポヒ。ュラーなのが関数の 実引数を区切るためのものて、ある。 カンマ演算子とは別物て、ある。 これは たとえば次の呼び出しを考えてみよう。 fprintf(stderr, "Oops! can't open \ %s*n", filename) ; もしここて、用いられるカンマがカンマ演 算子て、あれば , fprintf に渡される引数は filename だけて、ある。つまりすべての関数 にふたつ以上の実引数を渡すことがて、きな くなってしまう。現実にはそのようなこと のないように引数の区切りはカンマ演算子 とはみなされない。もし , 引数の部分て、ど うしてもカンマを用いたくなった場合には どうすればよいだろうか。その場合は , カ ッコて、くくればよいことになっている。 この例てはカンマが三つあるが , 中央の カンマはカンマ演算子て、ある。したがって , f00 に渡される実引数は a, c, d の値にな る。ところて、 , どうしてこのような器用な ことが可能になるのだろうか。それは「式」 の定義の工夫による。まず , 一般の式は次 のように定義されている (ANSI 3.3.17 ) 。 expresslon : assignment—expression exression , assignment—expression すなわち , このように読める。 式とは . ・代入式か ・式の後にカンマ演算子と代入式を並べ たものである 一方 , 関数呼び出しの定義を取り出して みると , それは後置式 (postfix-expression) の中て、定義される ( ANSI 3.3.2 ) 。 postfix—expression : pnmary—expresslon ( 略 ) postfix-expression ( argument- expression-list ) すなわち , 後置式とは 0 が 1 次式かまたは ( 略 ) 後置式の後に ( と省略可能な引数式リス ・引数式リストの後にカンマと代入式を ・代入式か 引数式リストとは . assignment—expression argument—expression—list, assignment—expression argument—expression—list : される (ANSI 3.3.2 ) 。 こて、 , 引数式リストは次のように定義 トと ) を並べたものである してもよい if (a > 0 ) 124 C MAGAZINE 1991 8
特集△認プログラミンク入門 List 3 と接åID を返します。ステータスパイト は , あるドライプがローカルドライプかネ ットワークドライプのいすれか , またはま ったくマップされていないかを返します。 ステータスパイトはディレクトリハンドル とネットワークドライプが永久的または一 時的に割り当てられているかどうかを知ら せます。 driveNumber 変数にすて、にドライ プ番号が格納されているのて、 , これを元に ドライプ情報を取得します。 バへの接続番号とディレクトリハンドルを 取得します。 バへの接続 ID は , サーバがログイン したユーザを管理す際に用いる番号て、 , 以後サーバのサービスを受ける際にはほと んどの関数て、この接続 ID を使用します。 ディレクトリハンドルは , ディレクトリ をサーチするためのポインタテープルて、す。 ファイルサーバはログインしている各端末 のディレクトリハンドルテープルを維持し ています。 このディレクトリハンドルテープルには 255 のエントリがあり , それぞれがポリュー ムやディレクトリバスをポイントするよう に設定て、きます。端末がディレクトリハン ドルをアロケートすると , そのファイルサ ーバはリクエストを出している端末のディ レクトリハンドルテープルに指定したディ レクトリのボリューム番号とディレクトリ 番号を入れます。 次にこの端末上て、稼働しているアプリケ ーションは , ディレクトリハンドルテーフ ルのインデックスて、あるディレクトリハン ドルを使ってディレクトリへ参照すること がて、きます。 GetDirectoryPath 関数 ディレクトリバスの取得 ディレクトリハンドルがわかったのて、こ の値からディレクトリバスと取得します。 このとき取得するディレクトリバスは次の ようになります。 ポリューム名 / ディレクトリ名 \ ディレクトリ名・ 例 ) sys/public 150 : 151 : 152 : 153 : 154 : 155 : 156 : 157 : 158 : 159 : 160 : 161 : 162 : 163 : 164 : } 165 : 166 : / * ドライプ番号を取得する * / 167 : BYTE 168 : GetCurrentDrive(void) 169 : { / * ドライプ番号 * / 170 : BYTE nwdrive; 171 : nwdrive ニ getdisk ( ) : 172 : return ( nwdrive ) ; 173 : 174 : 175 : 176 : VOid 177 : GetEntryPath (void *entry, char *path) 178 : NWDIR_ENTRY *dentry ま entry; 179 : 180 : strncpy(path, dentry->name, dentry->name し ength) : 181 : path[dentry->nameLength] 182 : 183 : } 184 : 185 : / * オプジェクト名を調べる。オプジェクト名がない場合は " Unknown " とする * / 186 : VOid 187 : Get0bjectName (long id, char *name) 188 : 189 : WORD type; 190 : int complet ionCode; 191 : &type) ; compIetionC0de ニ GetBinderyObjectName(id, 192 : name, if (completionC0de ! ニ 0 ) { 193 : strcpy (name, "Unknown") : 194 : 195 : 196 : } 197 : 198 : / * ファイル名を分解する * / VOid 199 : F ⅱ eNameSp ⅱ t ( char *path, char *name , char *ext ) 200 : 201 : { char drive[ MAXDRIVE ] , dir[ MAXDIR ] : 202 : 203 : fnsplit( path, drive, dir, name, ext ) : 204 : 205 : } 206 : 207 : / * ファイルの属性を表示する * / 208 : VOid 209 : ShowAttr i butes ( し ONG attr i butes) 210 : struct fileattlibute { 211 : value; 212 : long 213 : char *On; *off; 214 : Char 215 : 216 : 217 : 218 : 219 : 220 : 221 : 222 : 223 : 224 : 225 : 226 : 227 : 228 : 229 : 230 : 231 : 232 : 233 : 234 : 235 : 236 : 237 : } 5 ) & 0X000f , fentry. creationDate & 0X001f ) : / * 属性の表示 * / ShowAttr ibutes (fentry. attributes) : (fentry. modifYTime & 0X001f ) * 2 ) ; / * 所有者の表示 * / Get0bjectName (fentry. ownerID, owner) : printf("%-9s " owner) : / * 最終変更者の表示 * / Get0bjectName (fentry.IastModifierID, modifier) : printf("%-9sYn", modifier ) : } while (l); struct fi leattlibute fa[] FA READ_ONLY, FA_SHAREABLE, FA_NEEDS_ARCHIVED, FA HIDDEN, { FA_-SYSTEM, int i; printf( " for (i = 0 : i く 5 : i + + ) { if ( attributes & fa[i]. value ) printf("%s", fa[i]. on ) ; else { printf("%s", fa[i]. 0ff ) : printf( " ] 67 特集 LAN プログラミング入門
maruden. C List 以下のプログラムは , terminfo の端末情 報を使用する curses ライプラリを使って , 端 末て、のカーソルキーの使用を可能にすると ともに , カラー , 罫線を表示するプログラ ムの例て、ある (Fig. 3 , 4 参照 ) 。罫線は JIS コ ードのグラフィック文字を使用している ただし , グラフィック文字はメーカーによ って対応するコードが異なっているのて , プログラムの中て、環境変数 TERM を参照 し , 端末が PC ー 9801 て、あれば NEC のコード 体系に , そうて、ない場合には東芝のコード 体系に指定されるようにしている。 markndb3. c(List 1 ) main 関数て、ある。本関数は , データベー スのパラメータファイルをオープンした後 , ポックスカーソルによる選択が可能なメイ 端末に罫線を表示する プログラム 1 : #define EXTN extern 2 : 3 : #include ” ctstdr. h ” 4 : #include "compiler. h ” 5 : 6 : #include く string. h> 7 : #include く ctype. h> 8 : #include く ma 日 8. h > 9 : 10 : #include "ctoptn. h" 11 : #include ” ctstrc. h ” 12 : #include ” ctisam. h" 13 : 14 : # i fdef CTSERV ER 15 : #include "ctcomm. h" 16 : extern UCOUNT cts_apxsiz; 17 : #endif 18 : 19 : #include ” markndb2. h ” 20 : #include ” marknva2. h ” 21 : #include ” renta101. h ” 22 : 23 : #include く curses. h> 24 : TEXT *TFRMKEY() : 25 : 26 : maruden ( ) 27 : { 28 : COUNT rntupdat() : 29 : VOID cpychr() : 30 : 31 : COUNT fldno, action, keyno; 32 : TEXT *tarval : 33 : TEXT choice 幻 : 34 : TEXT cuname CUNAME い 1 ・ 35 : TEXT pdname PDNAME い 1 ・ 36 : TEXT baika[PDBAIKA い 1 ・ 38 : clear() : 39 : dendsp 1 40 : refresh 41 : echo ( ) : 42 : mvgetstr ( 2 , 18 , Gcu i npbuf) : 43 : noecho ( ) : 44 : i f (srcuname (Gcu i npbuf, cuname) ) { mvprintw ( 22 , 5 , " 該当するコードがありません " ) : 46 : getch() : 47 : return ← 1 ) : 48 : 49 : mvpr i ntw ( 3 , 18 , "%s". cuname) : 50 : getch ( ) : ech0() : mvgetstr(), 5 , Gpdinpbuf) ;noecho() : if(srpdname(Gpdinpbuf, pdname, baika) ) { 52 : 53 : mvprintw ( 22 , 5 , " 該当するコードがありません " ) : 54 : getch() : 55 : return(-l) : 56 : 57 : return ( の : 58 : } / * message S i ze * / Fig. 3 ーくん D 日 > > くく 処理を選択して下さい 年度末処理 第求処理 願客マスター 商品マスター っ一 3 ~ 宀物 0 1 ・ utextke4. c List 「エ 、ノ十》 . C ワ 0 - 十し X a. 0 十》 1 ーっ 0 O a. a. a. a. ′丨っこ′ー、戸ー L. L. L. E , 0 ( 0 ・し十し十し十し十し十レ ー 0 ー 0 X 十し・ - 、し囲 X a. O X 、 1 っ 0 っ 0 -4 ・ 0 ^. 0 7 ー 8 0 •—•っ朝っ -4 ・′ 0 っー 8 01 ーワレ " 4 ・【 0 0 ー 8 11 ・ー・ 11 1 1 、 1 1 よ 14 11 1 ーワ 0 ワ 3 ワ乙ワ】ワ 0 っ 0 ワ】ワ 3 ワ】 ・客コード : 客名 ード 商品コ 品 名 日付 : 博 9 ト 5-15 ( 土 ) 伝県番号 . 一区分一数量ー単 価ー金額 104 C MAGAZINE 1991 8
特集生△認プログラミンク入門 ス API を使うことにより直接プリントキュー を設定て、き , ネットワーク上のプリンタに 出力て、きます。 プログラム中て、使用しているプリンタキ ューの名前は、、 PRIN TQ 0 〃と固定してい るのて、 , 実際にプログラムを動かす場合に は、、 PRINTQ 0 〃というプリンタキューを 作るか , 、、 PRINTQ 0 〃の部分を書き換えて から再コンパイルしてください プログラムは大きく次のように流れてい きます ①現在のキャプチャーフラグの値の取得 ②フラグの値をセット ③キャプチャーフラグをセット ④現在のローカルプリンタの ID を取得 ⑤プリンタキューの ID を取得 ⑥サーバへの接続番号の取得 ⑦キャプチャーのセット ⑧キャプチャーの起動 ⑨ファイルをプリンタにリダイレクション て、出力 トしています。 このプログラムて、は印刷部数を 2 部にセッ フラグの値をセット 参照 ) を使用します。 理するための構造体 ( List 5 および Table 5 の内容を読み込む際に , キャプチャーを処 この関数は現在のキャプチャーのフラグ があります。 プログラムの最後て、最初の状態に戻す必要 め , ますキャプチャーの状態を調べておき , プチャーを終了させるようになっているた セットして印刷が終了したら自動的にキャ このプログラムは勝手にキャプチャーを 取得 キャプチャーフラグのデフォルト値を GetDefauItCaptureFlags 関数 値の取得 現在のキャプチャーフラグの ⑩デフォルトのキャプチャーの値に戻す ⑩キャプチャーを終了させる キャプチャ ラグをセット SetDefaultCaptureFlags 関数 ー一変更したフラグの内容をキャプチャー にセット キャプチャーフラグの内容をキャプチャ ーにセットするためには List 6 に示す構造体 を使用します。 構造体そのものの構造は CAPTURE FLAGS とほとんど同じて、すが , formName キャプチャーを処理する構造体 lSt typedef struct { 以降のフラグについては設定不可能となっ ています。 現在のローカルプリンタの 番号を取得 キャプチャーとプリントキューの設定を する際にデフォルトのプリンタ番号が必要 となります。 GetDefaultLocalPrinter 関数 プリンタ ( LPT ) の番号を取得 キャプチャーコールに使われるデフォル BYTE BYTE BYTE BYTE BYTE BYTE BYTE Char BYTE BYTE int BYT E i nt int char BYTE BYTE BYTE Char Char BYTE BYTE BYTE BYTE long WORD status; flags; tabSize: / * ③ * / serverPrinter; numberOfC 叩 ies; / * ⑤ * / formType ; reserved : bannerText [ 13 ] : / * ⑦ * / reserve; local し PTDevice; / * ⑧ * / fIushCaptureOnDev iceClose; / * ⑩ * / fIushCaptureTimeoutCount; / * ⑨ * / formName[13] ; maxChars : maxLines; LPTCaptureFlag: / * ⑩ * / fiIeCaptureFIag; / * ⑩ * / timingOutFIag; far *printerSetupBuffer; / * far *printerResetBuffer; / * connect ionlDQueuePrintJob; capturelnProgress : printQueueFIag; printJobVaIid; printQueueI D; printJ0bNumber; / * ⑩ } CAPTURE_F し AGS ; キャプチャーをセットする構造体 typedef struct BYTE BYTE BYT E BYTE BYTE BYTE BYTE Char BYTE BYTE int BYTE int i nt Char BYTE status; flags; tabSi ze; serverPrinter; number0fCop i es : formType; reserved ; bannerText[13] ; reserve; localLPTDevice; fIushCaptureTimeoutCount; fIushCaptureOnDeviceClose; max し ines; maxChars; formName[13] : PAD[21] : } SET_CAPTURE_F し AGS; 特集 LAN プログラミング入門 71