Standard このアプローチは , 露骨なところも微妙なところも含め , い くつかの点で標準に適合しない。 く cstdi0> をインクルードすることは , グローバルな名前空間 で名前を定義しないことになっているが , このバージョンでは 定義してしまっている。微妙なものとしては , ライプラリの名 前が間違った名前空間で始まっているので , 関数の多重定義の 解決が , あるべきように正確に動作しない ( ここでも詳細は適 当に済まさせてもらうが ) 。しかし , ほとんどの必要を満たす には十分近いところにある。 私の推奨は , 実装の間のさまざまな違いを最大限吸収するス タイルを採用することだ。グローバルな名前空間で定義された 名前が疑いなく必要ならば , <stdio. h> をインクルードすること だ。名前空間 std で定義された名前が疑いなく必要ならば , く cs tdi0> をインクルードすることだ。そして , これらの名前がも う一方の名前空間でも同様に定義されていることを常に想定す るようにする。これは C + + 標準規格ではない。しかし , 予見で きる未来を生きるための生活の知恵だ。 私は常にメモリ割り当てを List 5 のように注 意深くチェックしています。しかし , く recov er> のコードに行かずに実行を中断してしまいます。 現在 , べンダの中には , 標準 C ヘッダだけを別に取り分けて いるところがある。彼らはすべての名前をグローバル名前空間 で定義し , それをそのままにしている。く cstdi0> のようなヘッ ダは , 第 2 の自我であるく stdio. h > を単にインクルードするだけ だ ( これが現在の Micr 。 so れのやり方である ) 。別のべンダは , 標準 C ヘッダを別に取り分けているが , 新しいへッダについて はおおむね C + + 標準規格が要求するとおりにしようと試みてい る。このアプローチに従うと , ヘッダく cstdio > は次のような感 じになる。 #include く stdio. h> namespace Std { / / グローバルな名前を引き込む using ::FILE; 私は何か悪いことをしているのでしようか ? 何も悪いことはしていない。水面下でルールが変わって △ しまっただけだ。初期のころの C + + は , 標準 C ライプラ リの ma Ⅱ oc と同様に , 式 new は割り当てに失敗したら空ポイン タを返すことになっていた。しかし , C + + 標準規格はこれとは 異なる動作を要求している。現在は , 式 new は割り当ての失敗 をクラス bad ー a Ⅱ oc の例外を発行することで示すようになって いる。捕捉されない例外はプログラム実行を中断させる。 リカバリコードを実行させるようにコードを直したいのなら ば , やり方は 2 つある。 1 つは , 例外処理の世界に移行すること だ (List 6 ) 。 もう 1 つは , new の新しい ( が従来どおりに動作する ) バージ ョンを使用することだ ( List7 ) 。技術的には , この引数により , new は「 placement new 式」になる。 placement new 式では追加の情報をこっそり持ち込むことが できる。この場合には , nothrow はクラス nothrow t の定数で ある。この定数の目的は , この式が例外を発行せずに空ポイン タを返すようにさせることだけだ。これで古き時代のような動 作が実現される。 名前空間の使用など , このように , バックワードコンパチビ リティが損なわれていることは , 実に残念なことだ。どちらの 場合にも , 標準化委員会は , 既存のコードを無変更で動作し続 けるようにする必要よりも , ( 彼らが考える ) より安全なコード の書き方のほうを好んだ。おそらく , 将来 , プログラムは平均 的にはより安全になるだろう。それまでの間 , 多くの C + + プロ グラマは頻繁に質問し続けることになるのだろうが。 Thing *p = new Thing; } { 〃割り当てを試みる t て y List #inc lude く neW> 例外処理で対応する #inc lude く new> List <recover> } { / / 割り当ての失敗 catch (bad—a lloc ) new の新バージョンを使用する く recover> if (p = Thing *p = new(nothrow) Thing; 74 < 「 ecove 「 > のコードに届かない List Thing *p = new Thing ー く recover> C MAGAZINE 2001 3
p = malloc(size); if (p = = N も ) { fprintf(stderr, ex 辻 ( 1 return p ー / * 作業用の static 変数 * / static char *bufp; / * TAB を読み飛ばす * / char *skiptab(char *s) while (*s s 十十一 return S ー ”メモリが足りません n ” List *p ー void add—Iist(List * は s セ ) / * リストの適当な部分に要素を追加する * / return buf; / * 確保した領域を返す * / bufp = 町 s 十十一 while (*s / * 次の処理のために TAB を読み飛ばしておく * / *s = ch; / * 覚えていた文字を元に戻す * / strcpy(buf, bufp); / * 文字を確保した領域にコピー * / buf = my-malloc( 【引 / * 領域を確保する * / ch = *s ー / * こ にある文字を覚えておく * / ex 辻 ( 1 fprintf(stderr, ”データが異常です n ″潺 / * 全然進まなかったというのは何かおかしい * / if (s = = bufp) { s 十十一 char ch; / * 一時保存用 * / char *s = bufp; char *buf; / * malloc した領域へのポインタ * / char *get—fiel d ( VOid ) / * bufp の指す先から 1 つの項目を調べ、領域を確保してそここコヒ。ー * / に追加 * / if (strcmp(p->next->email, list->email ) > 0 ) ( / * こ fO て (p = は 8 セー top ー p->next ! = NULL; P = p->next) { } 引 se { list; ー ist—top ー if ( list—top = = NULL Ⅱ (strcmp(list—top->email, list->email ) > 0 ) ) { は s = p—>next ー break; bufp = buf; List *list; void add—data(char *buf) / * リストにデータを追加する void read—file(const char *filename) / * データを読む * / add-list(list); / * リストの要素を追加する * / list->email = get—field( list->tel = get—field( 潺 list->name = get—field( は s し = my—malloc(sizeof(List) fp = fopen(filename, fprintf ( stderr , ex 土し ( 1 "%s を開くことができません″ , filename); while (fgets(read—buffer, READ—BUFFER—SIZE, (p) ! = NULL) { if (*read-buffer = = ' Ⅱ *read—buffer = = ' 如 ' ) { continue ー add-data(read-buffer)i fclose(fp); int main ( void ) List *list; read—file(filename); list->next) { fo て ( list = list—top; list ! = NULLS list printf( ”宿ー 20S 宿ー 20S list->name, list—>tel, list->email return 0 ー データの例 大野 055 ー 5555 ー 5555 ohNo@0000 ℃ 0. jp 江藤 1 86 ー 090 ー 1234 ー 5678 eto@furukawa.com 上田 090-1333-3333 ueda@ueda.ueda ℃ om 伊藤 02 ー 2222 ー 2222 ito@xyz.aaa.0 「 . jp 安藤 01 ー 1111 ー 1111 ando@xyz.aaa.0「.jp # 名前 <tab>æ話番号 <tab>e-mail アドレス ※空白はタフ 1 っとする 答え 【 2 】 [ 問 2 司 【 3 】 【 4 】 ほかのプログラム言語とは別に , とくに C 言語でプログラム を書くにあたって , あなたが日ごろ気をつけていることを 1 分 以内に書きなさい。 答え ( 5 点 ) FILE *fp; 1 6 C MAGAZINE 2001 3
lllqå ~ グ末 ロ期 [ 問 2 List17 のプログラムを実行した結果画面に表示されるものを , 次の① ~ ④の中から選びなさい。 ① f 1 f2f3 1 ② f2f3f 1 1 ③ 1 1 ④ ( わからない ) 【 4 】 【 3 】 【 2 】 char へのポインタ p があり , 実際はこのポインタの指す先の 内容が int の配列になっていることがわかっている。すなわち , ( 3 点 ) *((int * ) p ) の結果の値は , 意味のある整数値が入っているも のとする。このポインタが現在指している整数値の次の値を指 すためには , どのようにすればいいか。次の① ~ ④の中から選 びなさい。 ② ④ p + = sizeof(int) ; 3 p + + ; 問 24 List #incl ude <stdio. h> 土 n セ fl(void) p て土 n セ f ( ” fl ” return 1 ー int f2(void) printf("f2" return 2 ー ( 2 点 ) 土 n セ f3(void) printf( ” f3 ” return 3 ー [ 問 25 List 19 は , 名前 , 電話番号 , e - ma ⅱアドレスから構成される データをファイルから入力し , e-mail アドレスの順に画面に表 示するプログラムである。入力するデータファイルは , タブで 区切られた名前 , 電話 , e-mail アドレスからなる行を羅列した 内容を想定している (Fig. 1 ) 。読み込むテータの厳密なチェック は行わないが , 行が「 # 」で始まる場合と , 空白行は無視するこ ととする。【 1 】 ~ 【 4 】に入れるものを , 次の① ~ ④の中から選び なさい。また , 【 5 】に記述すべき内容を書きなさい。 ③ list_top 4 list->next 2 p->next ( 【 1 】 ~ 【 4 】 : 各 3 点 ><4 , 【 5 】 : 5 点 ) 土 n し main ( void ) int i = fl ( ) Ⅱ f2( ) f3 ( 潺 printf( ” 宿 d n ” , 土 return 0 ー 問 23 可変個の引数を扱う関数を stdarg. h を利用 List 18 の関数は , して作成したものである。【 1 】 ~ 【 4 】に適切なものを , 次の① ~ ④の中から選びなさい。 1 va start 2 va end 3 va_arg 4 va list ( 各 1 点 X4 ) ① p 〇 List #include <Btdio. h> #include く 8 セ引 ib. h > #include く 3 セて土 ng. h > typedef struct list { struct ー ist *next ー char *name ・ char *tel; char *emai 嵭 〇〇〇〇 List / * 複数の文字列を改行して表示する。 * 最後は LL を指定する。 * printfx()—行目” ニ行目” NULL); 響土れ c lude <stdio. h> #include <Btdarg. h> void printfx(char * 8 セて , char * 円 【 1 】 argp; 【 2 】 (argp, str); fO て (p = str; p ! = NULL; p = p て土 n ( 物 s n ” 【 4 】 ( 矼 gp } is char *filename = naddress. dat" L 土 *list—top = NULL; #define READ—BUFFER—SIZE 1024 char read—buffertREAD-BUFFER-. SIZEI; / * メモリを確保し、不足した場合は強制終了 * / void *my—malloc(size—t size) void * 店 【 3 】 (argp, 曲 * ) ) 1 5 プログラミング期末試験 C 言語編 特集実力チェック
プログラミング技術情報誌・ C マガジン MARCH 2001 Cover 7 〃い tra 鹿 0 〃秋山育 / D おな〃持田哲 C 0 N T E N T S Linux Programming Tips く第 6 回 > SystemV 旧 C セマフォ養成合宿吉井一友 Windows Programming Tips く第 15 回 > ショートカットと特殊ディレクトリのオープン tatsuya Java Programming Tips く第 8 回 > 配列のソートとサーチひぐべん工房 遊びのレシピ fo 「 WonderWitch く第 4 回 > 番外編ーインタブリタ有馬元嗣 真紀俊男のローテク講座 LOW Technique Course く第 61 回 > 2 次元 + + の巻 C 言語フォーラム 恥ずかしながらドジりました く最終回 > XML を葬り去る新ドキュメント規格岩谷宏 最新開発環境レホート C / C + + 対応自動テストツール「 C + + TestJ 西田啓ー ニュース MONTHLY HEADLINE C MAGA Bookends 学問のススメ千言万語く第七十五語 > きだあきら フィンローダのあっはれご意見番く第 105 回 > C マガ電脳クラブく第 120 回 > 吉柄員樹 READERS' ROOM CD-ROM Contents EDITORS' ROOM 本記事中のプログラム名 . システム名 .. CPIJ 名は一般に各メカーの登録商標です。とくに甬情 がない限り , 本文中では TM. ⑧マークは明記していません。 本誌およひ付録 CD 一日 OM に掲載されたすべてのプロクラムは著作権法上 . 個人で使用する目的以 外で無断で複製することは禁じられています。 " 実力チェック プログラミング 期末試験 C 言言吾扁出題・解説 : フィンローダ・・ C 十十言言吾編出題・解説 : 岡嶋大介・・・ Java 編 出題・解説 : 毛呂宗夫・ PerI 編 出題・解説 : フィンローダ・・ Ruby 編 出題・解説 : 後藤謙太郎 C 言語プログラミング学習塾 く最終回 > 今後の指針長久勝 Q&A プログラミング相談室 く配列のメモリ確保 ! 乱数の発生 , printf と puts, 引数の数 > 毛呂宗夫 Standard C/C + + TrequentIy Answered Questions 』 P. J. 円 auge 「 / 熊谷典大訳 Enjoy PerI Programming モジュールを活用しよう く第 2 回 > メール送信 CGI と Base64 工ンコーディング結城浩 伝授 ! 極めよ Ruby 道 く第 8 回 > スピードアップーインタブリタは遅いけどー 後藤謙太郎 Java プログラミング リファレンス詳説 JDK 解体新書 く最終回 > 式 (Expression)(13) きだあきら 画像処理を極める アルゴリズムラボ く第 18 回 > デジタル画像での図形描画④シードベイント 昌達 K'z Enter The 3D Programming く第 6 回 > 曲面あれこれ久保裕一郎 / 宇治社中 10 111 117 121 1 3 ~ 4 ~ ~ 125 131 136 144 148 153 156 158 160 162 164 166 63 68 70 75 81 90 特別付録 CD-ROM Microsoft . NET Framework SDK 1 日本語版 C C + + Test 1.0 トライアル版 0 Borland JBuilder4 アップデート 【 -Oava2 SDK Standard Edition Ver. 1.3.0 0 Ruby 1.6.2 , ほか C ル偽 G. Z / へ旧 員心わ既た s 行 0 ⅵョわョ″ S わ 9 Director: ke ルに訂 E けの ( - ⅵ -0 ツé J し序 0 Watanabe EO に 0 朝 nd Business Offices:4- 73 ・ 13 Akasaka, ん 4n ョこ 0- k し . TOKYO 70 / ・こ 52 い PA Ⅳ Eäに 0 朝な P わ 0ne035549 ・ⅱ 43 Sa!es: 氾 e ( 035549- ヨ 2 ) Adve 嘯 s わ g. ・ P10ne035549 ー 7220 Copy 行 9 02C つ 7 S ツョっ k R 心わのリ″℃ .. ー bk / 0. レ 0 な s resetved Ⅳ 0 日 : e 朝′ⅵ ! を Sp しし on ″ / be 田 ( メし ced , の義わ 0 0 戸 h ( 瀚のイツわ物 O ま′′ s & on. Printed の J 日日 n. 98 105
ん。ただ , 一般の利用に際しては , 十分す ぎるほどに大きいサイズをバッフアとして 確保しているので , 現実にはバッファあふ れを起こすことはほとんどないでしよう。 なお , 厳密にバッファあふれを避けるため には , 単方向リストにより , バッフアを構 成すべきでしよう。 List 4 のプログラム自体は , シードベイ ントアルゴリズムの基本的な流れに従って 処理を行うだけなので , とくに難しいとこ ろはないかと思います。画像内の走査は関 数 search_left で左端を求め , 関数 search_r ight で右方向へ走査を行いながら描画を行 っています こで , プログラムを解読するうえで , Pmacs プラグインの仕様からくる注意が 1 点あります。 pmacs プラグインでは , 画像 上の画素値を取得する際に API 関数 PMsGe tC010rOnImageGlobal を利用しますが の関数は , 描画前の画素値しか取得するこ とができません。ですから , すでに描画し た画素であっても , 古い画素値を取得して しまい , 何度も同じ画素に描画を行うとい う無限ループに陥る可能性があります。 List4 ではこれを避けるために API 関数 PMsSetDrawFIag を利用しています。これ は , 画像上にフラグを立てる関数で , 画素 値とは別にフラグ値をセットすることがで きます。これを利用して描画した画素のフ ラグを 1 にして , 領域内外の判断時にフラ グがすでに立っている画素を領域外とみな すようにします。このようにすることで , 同じ画素に繰り返し描画することを避けら れます。 次に , シードベイントアルゴリズムの高 速化バージョンを実装してみます。まず , 走査時の処理軽減を行うため , 座標を記録 するバッフアに加え , ・前の走査線との上下関係 ・前の走査における右端座標 の情報を記録するためのバッフアを用意し ます。これらの情報に従って , 走査時に調 べなくてもよい部分を特定し , 不要な処理 を削減できます。高速化のための処理は , right—UP ・・ 塗りつぶし処理をしながら右方向へ走査す る部分で行われます。 List 4 では右方向の 走査を関数 search-right で行っていますが 付録 CD - ROM 収録の高速化ノヾージョン (pa ⅲ皀 c ) では , 不要な画素チェックを効率的 に削減するために ・ search_right_both ・・・・・・上下両方の ・ search ・ search right_ down ・・ の ・・・上の画素の みをチェッ クするもの ・・・下の画素の みをチェッ クするもの 画素をチェ ックするも 機能 の 3 種類に分けて用意し , 条件によって振 り分けています (List 5 ) 。 TabIe 1 主な PmacsAPl 関数 関数名 引 POINT * pt 数 PMsSetNewEntryPoint 座標をバッフアに記録する 今までバッフアに記録した座標の数を PMsGetHowManyPoints PMsGetEntryPoint PMsStartPSet PMsPSet PMsSetDrawFIag PMsGetDrawFIag PMsEorPoint 求める バッフアに格納した座標を取り出す PMsPSet に対する準備処理 画像上の 1 画素上にカレント色で描画 する 画像上にフラグをセットする ( フラグ 情報は Pmacs 本体で管理されている ため , プラグインは気にする必要はな 画像上のフラグを取得する 画素を反転表示する ( この処理結果は 表示のみなので , 元の画像には影響を 与えない ) 記録する座標情報 なし int n 取り出す座標の番号 ( 最初に登録し た座標を O としあとは連番 ) POINT * pt 座標を格納するための領域 なし EdtWin * edt 描画を行う画像が表示されている ウインドウ intx, y 描画を行う座標 i nt col カレント色 ID , 通常は 0 O : 描画色 1 : 背景色 EdtWin * edt 描画を行う画像が表示されている ウインドウ intx, y 描画を行う座標 int flag フラグの値 ( 00 「 1 ) EdtWin * edt 描画を行う画像が表示されている ウインドウ intx, y 描画を行う座標 int flag フラクの値 ( 00 「 1 ) EdtWin * edt 描画を行う画像が表示されている ウインドウ intx, y 描画を行う座標 int del TRUE : 反転表示 FALSE : 元に戻す 102 C MAGAZINE 2001 3
踟 mm 加 List 1 特殊ディレクトリのオープン こまで説明したようなシンプルなショ ートカットの場合 , 基本的に ShellExecute 関数を用いることで , そのファイルのオー プンや実行が可能です。ですが , ダイヤル アップネットワーク , プリンタ , コントロ ールバネルといったシステムに直結した特 殊なディレクトリについては , その引数の 設定に注意が必要です。 たとえばダイヤルアップネットワークの 場合 , List 1- ⑩のような記述をします。 の引数で指定されている「 : : { 20D04FE0 ~ } : : 」 の { } で囲まれた部分が , 前半で説明したク ラス修飾子 , CLSID です。 Windows では , ダイヤルアップネットワークのフォルダに はこの ID 番号が関連付けられているのです。 その情報を保持しているのは , これも前半 で説明したとおりレジストリです。プリン タフォルダも同じように指定 , 表示してい ます (List 1- ⑩ ) 。 なお , ほかにも下記のような C ID があ ります。 マイコンピュータ { 20D04FE0-3AEA -1069- A2D8-08002 B30309D } else { SendMessage(GetDIgItem(hDIg, IDC—LISTI) , LB—ADDSTRING, 0 , い ong 尸 Load 失敗つデ メールのオープン VOid openmaiI(HWND hDIg) ” m に 0 : tatsuya@exnetcom ・ eom?subject=TEST% NUIÄJi U もも , web のオープン Vöid openweb ( VOid ) s い u ( 部いい心い呷址セノ : ん、沺 e セ朝い痴もい ダイヤルアップフォルダのオープン void fol deropen ( void ) h 4 x c 沚 eÅ阯い当 63h ! / ーを宿 { 20D04FE0 ー 3AEA -1069 ー A2D8 ー 08002B30309D 当 : 992CFFA0 = を 557401 98 を 0 ー 000D010CCC48 戸 プリンタフォルダのオープン void fO lderopenprinter ( void) 取もしいド 08 い EXPLOR 良を ! : ( 20D04FE0 ー 3AE ト 106 A2D8-08002B30309D } ! 叔 2227A280 ー 3AE 1069- A2DE -08002B3030SP 戸 ネットワークコンピュータ { 208D2C60-3AEA -1069- A2D7-08002 TabIe 1 利用した主な A 曰 B30309D } HINSTANCE ShellExecute(HWND hwnd. LPCTSTRlpOperation, LPCTST 日 IpFiIe, 興味のある方は調べてみてください。 LPCTSTR lpParameters, LPCTSTR lpDirectory,INT nShowCmd); 説明指定されたファイルのオープン ( 表示 , 印刷 ) / 終わりに 親ウインドウのハンドルを指定する hwnd lpOpe 「 ation 実行する操作が入った NIJLL で終わる文字列へのポインタ F ⅱ e パラメータで指定したファイルをオープン open" 今回は ShellExecute 関数を使って , ショ F ⅱ e バラメータで指定したファイルを印刷 p 「 int" ートカットや特殊なアプリケーション , テ F ⅱ e バラメータで指定したフォルダを調べる "explO 「 e" 返り値成功すると 0 よりも大きな値 , 失敗すると 0 以下 ィレクトリの実行を解説しました。 HRESULT CoInitialize (LPVOID pvReserved) ・ 最近とくに需要が増えている Web へのア 説明コンポーネントオプジェクトライブラリの初期化 クセスは , ショートカットを作成するのも 引数 pv 日 eserved 予約済み。 NULL でなければならない 1 つの手ですが , プログラム内部で処理して しまうほうがメニューにも組み込みやすく うため , 今回は ShellExecute 関数で直接オ スをオープンする場合 , 方法は 2 通り考え なります。よぶんなファイルも作られずに られます。プログラム内部から直接起動す ープンする方法を採用します ( List1- ⑩ ) 。 すっきりするのではないでしようか。 る方法と , x . url のようなインターネット このように , ShellExecute 関数に URL ア 昨今ではメールや Web へのアクセスは必 のショートカットをいったん作成し , これ ドレスを記述することで , Web プラウザの 須事項になりつつあります。いろいろな方 を She Ⅱ Execute 関数で実行する方法です。 起動とともに目的の U Ⅲ , へ接続するという 法でアクセスを促すことで , 次へ向けた展 シンプルなインタフェイスが可能になりま 後者の url ファイルを使う方法もいいのです 開が見えてくるでしよう。 す。 が , 新たに別ファイルが必要になってしま 1 20 C MAGAZINE 2001 3 引数
LiIlllX l)!()!fillllllllll! TIPS List 4 List 完成したセマフォにより排他処理を行う例 ( sem. c ) semaphore(p); access—shared_resource ( ″ [ ″ ) ー fo て ( j = j く (rand( 厖 10 ) 十場十十 ) { rand-sl eep ( access—shared—resource ( msg ) ー access—shared—resource にド ); semaphore(V); rand—sl eep ( #inc lude く stdio . h> #inc lude く stdl ib. h> #include <unistd . h> #include く sys/types. h> #include く sys/wait. h> #inc lude く sys/ ipc 上 > #inc lude く sys/ sem. れ > #include く e てて no . ね > static int semid ー static void error(char *msg) { if(msg) perror(msg) ー exit(l) static rand—sleep(void) { sleep(rand( ) 宅 2 ) static VOid access—shared—resource ( char *msg ) if(msg) printf( "%s",msg) ,fflush(stdout); int main ( int argc , char *argv [ ] ) int pid , status ー short initva ー srand ( ( unsigned int ) getpid ( ) if( (semid = semget( ( key ー t ) 0113 , 1 , 066 引 IPC-CREAT) ) = -1 ) er て 0 て ( ” semge ビ if( semctl (semid, 0 , SETALL, &initval ) er て 0 て ( nsemctl" リ if ( (pid = fork( ) ) く 0 ) error( ” fo て k ” else if (pid = = 0 ) { do-dangerous("c"); ex 辻 ( 0 } 引 se { do-dangerous ( ″ #define P ( -1 ) #define V ( 十 1 ) static VOid semaphore ( int operation ) struct sembuf semb ・ semb. sem-num = の semb. sem—op = operation; semb. sem—f 地 = SEM—UNDO; if( semop(semid,&semb,l) ! = 0 ) error( ” semop wait ( &status access—shared—resource ( ”基 n ″ e てて or ( nsemct ド if( semctl (semid,0,IPC-RMID) ! = 0 ) return 0 ー static VOid do—dangerous ( char* msg ) int i , rand—sleep( fo て ( i = 土く 3 ー i 十十 ) { 力を占有している ) ことになります。 て出力を得るにはどのような実装を行った int semflg ) らよいでしようか。 1 つの解決策がセマフ のようになります。 このルールで排他的なアクセスが行わ オによる排他アクセスの制御機構です。 これを使って List 1 を書き直したものが れている場合 , 標準出力に出力された 文字列中で ' [ ' , ' ] ' のネストは存在し List 2 になります。この例では , 第 1 引数 セマフォの導入 key に渡すキーは直接整数を指定していま ません。 すが , 関数面 k ( ) によって作成されたキー ちなみに , この例では , 各プロセスが 3 回標準出力を占有して , そのたびに数個の を使ったほうがよいでしよう。第 2 引数 ns では List 1 にセマフォを導入して , 共有 ems には , セマフォの数を指定します , バ 文字を標準出力に出力します。出力タイミ リソースを排他的にアクセスできるように イナリセマフォの場合は 1 になります。第 ングはランダムに遅延されるようになって してみましよう。 います。 まずは共有リソースを持つプロセス間で 3 引数 semflg には , セマフォへのアクセス 共通のセマフォを作成します ( 正確にはセ 許可および作成モードを指定します。 1 つ ではさっそく , List 1 をコンパイルして のプログラムから関数 fork ( ) によりプロセ マフォ集合の識別子を取得する ) 。セマフ 実行してみましよう。予定どおり標準出力 の出力結果に ' [ ' , ' ] ' のネストが存在して オの作成は , 関数 ( システムコール ) semg スを起動するような場合は , このようなセ et() により行います。関数 semget() の書式 いて , 排他的なアクセスができていないこ マフォの作成でよいのですが , べつべつの とを確認できるはずです ( Fig. 2 ) 。 プログラムからプロセスが起動する場合 , は , 前述のルールに沿って排他的なアクセス int semget( 関数 semget ( ) に与えるフラグは異なってき ます。これについては , man/<—ジ semget が行われた場合 , Fig. 3 のような結果を得 key_t key, ②を参考にしてください。 たいところです。では前述のルールに沿っ int nsems, 1 1 3 Linux Programming Tips
Enjoy PerI Programming モジュールを活用はう しかし , 実際の WebAt ージではユーザに DynaLoader は C ライプラリをダイナミ とができるわけですから , この CGI を使っ ックにロードするためのモジュールです。 入力させるのではなく , 固定文字列にして てメッセージを受け取った人は , メールア おくほうが便利な場合があります。たとえ すぐあとで bootstrap という関数が使われ ドレスをうのみにしないようにする必要が ば質問のページなら " [Q] " という表題 , プ ていますが , これは DynaLoader モジュー あります。 ルからインポートした関数です。直後の if 文 レスリリースのページなら " [PRESS] " と 参考リンク で , C ライプラリをロードできなかった場 いう表題にしておけば , 受け取ったメール 合の処理が書かれています。 の分類・振り分けが楽になるからです。 もしも C ライプラリをロードできなかっ そんなときは「隠しフィールド」を使うと 以下の Web ページでもメールの送信お た場合には , old_encode_base64 , old_dec よいでしよう。たとえば次のようにします。 よび送信 CGI について詳しく解説していま ode—base64 という関数 ( これは直後に perl すのでぜひ参考にしてください。 <input で書かれている ) を encode_base64, decod type= ” hidden ” ・ ActivePerl でメールを送る (HiPP02000 さん ) e ー base64 というシンポルに割り当てていま name= ” mysubject" http://member.nifty.ne.jp/hipp02000/ す。 value= ” [Q] ” > perltips/perlmail. htm これによって , もしも MIME::Base64 の このようなタグをフォームに含めておけ このページでは , CGI という形ではなく , C ライプラリがその Perl 処理系でインスト ば , 自動的に表題が " [ Q ] " という文字列に 通常のスクリプトとして ( とくに ActivePerl ールされていればそちらを使い , さもなく なってくれます。 で ) メールを送信する方法についてサンプ ば PerI で書かれたコードを利用する , とい ルスクリプトが示されています。 表題の自動修正 う機能を実現していることになります。 ・ホームページにメール送信機能を付けて もしも , 自分の PerI 処理系がどちらを使 上で述べた方法だと固定文字列になって みよう (INTERNET Watch 特集記事 ) っているのかを確かめたかったら , Base6 しまいますが , プログラムの中で , http://www.watch.impress. CO. jp/internet 4. pm を修正して , if 文の近辺に List 11 のよ $form {mysubject} = /www/article/1999 / 1 122/special.htm うにデバッグプリントを入れてみるとすぐ "[Q] $form {mysubject} " このページでは , sendmail プログラムが にわかります。 のようにすれば , ユーザが入力した文字列 使えない場合の 2 つの代替手段についても の前に自動的に " [ Q ] " という文字列を挿入 書かれています。 1 つはファイルとして保 最後の部分 することもできます。 存しておく方法 , もう 1 つは SendmaiI. pm encode_base64 を , MIME::Base64::enco という別のモジュールを使う方法です。 送受信の両方を指定できないほうがよい de という名前で使用できるようにするため 次回は「ファイル添付」 に Base64. pm の最後のほうには List 12 の 次節の参考リンク (INTERNET Watch 特 ような代入がなされています。 集記事 ) の中でも述べられていますが , メ 最後の 1 ; という部分は , このモジュール ール送信 CGI では , 送受信の両方を外部か 今回はメール送信 CGI を作ってみました。 が正しく読み込まれたことを表現していま ら指定できるようにするのは危険です。悪 いかがでしたか。次回は , メールにファイ す。 use や require では , 最後に評価した式 ルを添付する機能を考えてみましよう。 意を持った人が , From も To も自分以外の が偽だとエラーとみなされるので , このよ もしも , ご意見やご質問がありましたら , アドレスを指定してしまうと , 他人になり うに 1 ; という真の値を評価するようにする すまして第三者にメールを送れることにな 本誌綴じ込みの編集部へのハガキでお知ら のが普通です。 せくだされば幸いです。また , ご遠慮なく ってしまうからです。 もちろん今回の selfmail. cgi でも , myfro 結城浩くhyuki@hyuki.com/ へメールをお送 りください。本連載に関するメールには表 m に第三者のメールアドレスを指定するこ 題に CEP] という文字を含めてくださると MlME::Base64 モジュール (3) 助かります。 本連載に関する U 糺は , 1 : *encode = %&encode—base64 ー 2 : *decode %&decode—base64; http://www.hyuki.com/ep/ 3 : 4 : です。 修正のヒントと注意事項 隠しフィールドの利用 今回の selfmail. html では , メールの表題 ( Subject ) をユーザに入力させています。 List 80 C MAGAZINE 2001 3
http://search.cpan.org/ search?moduIe=MlME. :Base64 ファイル Base64. pm は MIME というティ レクトリを作り , その下にド PP でコヒ。ー ( p ut ) します。もしもプロバイダがすでに MI ME::Base64 をインストールしている場合 は , Base64. pm をコビーする必要はありま せん。なお , selfmail. cgi は CGI モジュール ( CGI. pm ) も使いますが , CGI モジュール は Per15 の標準モジュールなのでここには 含めていません。 HTML のフォームで使う変数 HTML のフォームでは Table 3 の変数を 用います。例は List 1 (selfmail. html) を参考 にしてください。 設置手順 sel 血 ail. cgi の設置手順は次のようになり ます。 ・ ( 1 ) 自作 C 引が使用できるかどうかをプ ロバイダに確認 利用しているプロバイダが自作の CGI ( 0 wnCGI ) を許可しているかどうかを確認し てください。もしもプロバイダが許可して いないなら , 動作させることはできません。 許可している場合には , CGI の設置方法に 関する情報を収集し ( プロバイダによって 異なります ) , それに従ってください。 ・ ( 2 ) sendma ⅱが使えるかどうかの確認 selfmail. cgi は sendmail プログラムが利用 できなければ使うことはできません。自作 CGI は使えても , セキュリティ上の理由か ら sendmail を使えなくしているプロバイダ もあるので , 確認してください。 ・ ( 3 ) ファイルの修正 sel 朝 lail. cgi の 1 行目 (List 2 の 1 行目 ) の Per 1 処理系のパスを正しく指定します。それ から , se Ⅱ ail. cgi の始めのほうにある Tabl e4 の変数の値を修正します。修正 , のため , perl-cw selfmail. cgi ・ ( 4 ) ファイルの転送 とを確認しましよう。 と入力して , " syntax OK ' と表示されるこ use MIME::Base64 は , MIME::Base64 を のものです。 useCGI は , CGI モジュールを使うため ⅱ b は不要です ( やってもかまいませんが ) 。 をすでにインストールしているなら , この use るプロバイダが MIME::Base64 モジュール トリに置いたためです。もしも利用してい (MIME/Base64. (m) をカレントディレク れは MIME : : Base64 モジュールのファイル トディレクトリ (. ) を追加しています。 するためのプラグマです。ここではカレン uselib は , モジュールの検索パスを追加 を厳しくするためのプラグマです。 usestrict は , プログラムの文法チェック 系の場所を指定します。 冒頭の # ! で始まる行 (List 2 ) では Perl 処理 Pe のバスとモジュール sel lail. cgi を読んでみましよう。 スクリプトを読む いるはずです。 アドレスにその内容がメールとして届いて 押します。すると , $receiver で指定した で , 適当な文字列を入力してボタンを ると , Fig. 1 のような画面が表示されるの selfmail. html の URL をプラウザに入力す ・ ( 5 ) 実行 更します。 た転送後 , ファイルのパー、ツションを変 ます。転送モードに注意してください。ま 転送します。通常はドソフトなどを使い TabIe 2 に示すファイルを Web サーバに 可 OY PerI Programming モジュレを活用はう 使うためのものです。 MIME::Base64 は , メールの表題を Base64 工ンコードするた めに使っているだけなので , もしも表題に 英語しか使わないのであればこのモジュー ルは不要です。 require' jcode. pl" は , 漢字コードライプ ラリの jcode. pl を使うためのものです。 設定項目 次に sel ail. cgi の設定項目がきます (List 3 ) 。 変数 $back_to_home と $mail_to_admin はそれぞれホームページに戻るリンクと管 理者へのメールを表すリンクになります。 ここで a ( ・・・ ) という関数が使われています が , これは CGI モジュールからインポート したもので HTML のく A > タグを文字列とし て構成するためのものです。 実行 サプルーチン & init ー form でユーザからの フォームの情報をハッシュ % form に受け取 ります。そしてサプルーチン &do_send で メールを送信し , exit で終了します田 st4 ) 。 送信処理 サプルーチン & do send では , &send m ail ー to で実際のメール送信を行い , その後 「送信ありがとうございます」というべージ (Fig. 3 ) を構成します ( List 5 ) 。 ' こで使わ れている , start_html , hl , p , end_html という関数は , すべて CGI モジュールから インポートしたものです。 工ラー表示 サプルーチン &print_error(List 6 ) では , 工ラーが発生したときのページを表示し , 終了します ( 今回は使っていません ) 。 フォームからの情報入手 サプルーチン & ⅲ it ー form は , ユーザが入 力した情報を連想配列 ( ハッシュ )%form に Enjoy perl Programming モジュールを活用しよう / /
to ー p 仕があります。デストラクタで delete が 実行されるのは所有権のある auto_ptr だけ で , 所有権がなければとくに何もしません。 aut0_ptr<X> p(new X() ) ; auto_ptr<X> q = p, このようにすると , 1 行目では p が所有権付 きで初期化されます。次に 2 行目では aut 。印 仕同士の代入が行われますが , このときに 代入先 ( すなわち q ) に所有権の移動が行われ ます。その結果 , p は所有権を失うのでデ ストラクタでは何も行わず , q のデストラク タで delete が実行されるわけです。所有権 がある理由は , 1 つの生ポインタに対して は常に 1 回だけ delete を呼ぶようにするため です。そうでないと , delete 済みのオプジ ェクトに再び delete を実行してしまいます。 普通の代入だけならとくに混乱はないの ですが , 問題は関数の引数に auto ー p 仕が使 われた場合です。問題の関数 bar では引数 が auto-ptr であるため , 関数の呼び出し時 に新しい auto ー ptr のインスタンスが作成さ れ , ここに所有権が移動します。これは bar の実行が終わるときにデストラクタが実行 され , 中身のオプジェクトは delete されて しまいます。その結果 , f 。。でその続きを実 行したとき , 削除済みのオプジェクトに対 してメソッドを起動するため工ラーが発生 してしまいます。 引数に aut 。ー p 仕を使うとどうしてもこの ことを考えなくてはならないので厄介です。 所有権問題に頭を使わないようにするため には auto—ptr のコピーが起こらないように することが必要です。つまり , 引数に使う ときは auto—ptr そのものではなく auto_ptr へ の参照 , もしくは中身の生ポインタを使う ことです。たとえば生ポインタを使う場合 , void f00 ( ) aut0_ptr<X> p (new X ( ) ) ; p->some method ( ) ; void bar(X * p) p->some method ( ) ; と書き換えることができます。 とえば , PDA のような機器ではメモリ消費 の尺度の有効・無効は変わってきます。た 中身です。もちろん , 場合によってこれら といったことはみな「すぐれている」ことの ・移植がしやすい ・機能拡張がしやすい ・書くのに時間がかからない ・エラーを適切に表示してくれる ・仕様がシンプルである ・他人が読みやすい ・プログラムサイスが小さい ・メモリ消費量が少ない ・実行速度が速い つくだけでも , はいろいろな尺度があります。ざっと思い 体的にはどういうことでしようか。これに ところで , コードがすぐれているとは具 しようか ぐれている理由を指摘できたのではないで かりやすい例を出したので多くの読者はす サンプルプログラムでは簡潔にするためわ コードのデザインについての問題です。 ・解説 3 つ以上指摘できれば正解です。 もそのことに気づきにくい ・ B の modify は , 間違った引数を渡して を使うべき リークになってしまう。デストラクタ ・ B は明示的に free を呼ばないとメモリ ので余計 ・ B の init() で , 引数 s から取得できる いていない ・ B は初期化処理をコンストラクタに書 特集 プログラミング 期末試験 量を抑えることはとても大事ですし , 締め 切りが明日という仕事では書くのに時間を かけないことが最優先です。チームで作る 場合には読みやすいことが重要で , 大勢に 使ってもらう場合にはエラーを適切に表示 できないと大変です。さらに , これらの指 標はみなトレードオフの関係にあるので , どれをどれくらい優先するかが設計者とし ての腕の見せどころになるのです。 では , この問題の例で具体的に検証して みましよう。 B では , 単純に使うだけでも , mystringB s; s. init("HeIIo" 5 ) ; s. free ( ) ; という手続きが必要です。一方 A では , mystringA s("Hello") ; だけで済みます。 B を使う場合には , init で 文字列の長さも渡さないといけなかったり , 斤 ee ( ) を呼んでやらないといけないので仕 様が複雑になっています。また , このせい でプログラムの行数が増え , 読みやすさが 低下しています。 入門書には , オプジェクト指向の 3 原則 として , カプセル化・継承・ポリモーフィ ズムがよくあげられていますが , これはカ プセル化の領域に属します。 B は , 結局解 放のためのメソッド呼び出しが必要なので , カプセル化に失敗しています。一般には , 「何かの存在をオプジェクトを使う側に忘 れさせる」ことができたらカプセル化成功で す。 mystringA を使うと , メモリの確保・ 解放操作のことを意識する必要はありませ ん。デストラクタはコンパイラが適切に呼 び出すようにしてくれるので , 解放操作を ここに閉じ込めることが功を奏しています。 一方 , 工ラーハンドリングでも A がすぐ れています。 1 文字を書き換えるメソッド m od において , A は asse れによってチェック をしているので , もしこれにひっかかった 場合には診断メッセージとともにプログラ ムが終了します。一方 B では無効な引数が 特集実力チェックプログラミング期末試験 c + + 言語編 31