long - みる会図書館


検索対象: 月刊 C MAGAZINE 1992年9月号
19件見つかりました。

1. 月刊 C MAGAZINE 1992年9月号

箱 応 LASER SHOT 凵 PS 関数群⑤ go—to—vector (mode) char *mode ; fprintf(stdprn, "XcXcXsXcXc", CSII, CS12, mode, return( の ; List 2 List 2 vector var(rx) : vector_var ()y ) ; vector var(kaiten) ; fprintf(stdprn, "Xc", IS2) : return( の ; LONG labs(a) LONG a; if (a く OL){ return(a) : start—picture(n) Char *n ; fprintf(stdprn, "XcXsXc", ' # ' , n, IS2) ; return( の ; draw—picture() fprintf(stdprn,i'XcXc", ' $ ' , IS2) : return( の ; myline(xl, yl, x2, y2) LONG xl, yl, x2, y2; LONG xx, yy ; fprintf(stdprn, " 1 " ) ; vector—var(xl) : vector_var(yl) ; xx = x2 ー xl; YY = Y2 ー YI ; vector_var (XX) ; vector_var (yy) : fprintf(stdprn, ” Xc", IS2) : return ( の ; end—p i cture ( ) fprintf(stdprn, ” XcXc", ' % ' , IS2) : return ( の : return—to—text (p) POINT *p; fprintf(stdprn, "XcXc", ' ) ' , ' p' ) ; vector_var()- 〉 x vector_var(p->y) fprintf(stdprn, ” % c ” , IS2) ; return( の : 、ノⅣん ・ 1 O O 1 よ A O O O vector_var (data) LONG data ・ char sign, bYteC6] : SHORT i : if(data く (L) { sign = 0X20 ・ else{ sign ニ 0X30 ・ data = labs(data) : 1 byteCi + + ] = (char) (data & 0x0f ー sign) ・ data = (data 〉〉 4 ) ・ while(data 〉の { byte[i + + ] = (char)(data & 0x3f ー 0X4 の・ = (data 〉 > 6 ) ・ data while(i- fprintf(stdprn, ' 'Xc", byteCi]) ; re turn ( の ; line-width(width) LONG w idth : fprintf(stdprn, ” FI ” ) ; vector var (w i dth) : fprintf(stdprn, "Xc ” , IS2) ; re turn ( の ; mybox(xl,yl, x2,y2) LONG xl, yl, x2, y2; fprintf(stdprn, " } : vector_var ()l ) ; vector var(x2) : vector_var(yl) ; vector_var(y2) ; fprintf(stdprn, "Xc ” , IS2) ; re turn ( の ; zahyov—unit(unit, kakudai) LONG unit; LONG kakudai ; fprintf(stdprn, " ! 0 " ) : vector var(unit) ; if(kaküdai 〉 OL){ vector—var(kakudai) : fprintf(stdprn, "Xc", lS2) : return ( の : circle(), y, r) LONG x ; LONG y : LONG r; fprintf(stdprn, vector_var ( x) : vector_var(y) ; vector_var (r) : fprintf(stdprn, "Xc ” , IS2) : re turn ( の ; area—pattern(pattern, rinkaku, kaitenkaku, lrreverse, wbreverse) LONG pattern, rinkaku, kaitenkaku, lrreverse, wbreverse; fprintf(stdprn, "I") ; vector_var(pattern) : vector_var(rinkaku) ; vector_var(kaitenkaku) : vector_var(lrreverse) : vector_var (wbreverse) : fprintf(stdprn, "Xc", lS2) : return( の : daen(), y, rx, ry, kaiten) LONG x; LONG y; LONG rx; LONG ry ; LONG ka i ten : fprintf(stdprn, vector var(x) ; vector var (y) ; 応用 C 言語 133

2. 月刊 C MAGAZINE 1992年9月号

Fig. 1 6 円 , 四角 , 楕円 Fig. 13 circle( ) 関数の書式 circle (), y, 「 ) long x : long y , long 「 : ・ MS-C 用 config. h #define MSC #ifndef MSDOS #define MSDOS #endif #ifndef PROTOTYPE #define PROTOTYPE ←中心の x 座標 ←中心の y 座標 ←半径 Fig. 14 myb 。 x ( ) 関数の書式 mybox()l ,yl ,x2, y2) ←始点の座標 long xl,yl ・ ←終点の座標 long x2, y2 , #endif ■■ UNIX curses 道具箱に追加された関数を UNIX 上て、コン パイルし , 特定のライプラリ ( たとえば libcb ox. a) に追加するには , 以下のようにする。 ←中心の x 座標 /usr/5bin/cc func. c -c -g ←中心の y 座標 ← x 軸方向の半径 ar rv libcbox. a func. 0 ← y 軸方向の半径 CURSES ライプラリとリンクする場合に ←回転角度 は , コンパイル ( あるいはリンク ) の際にリ ンクオプションを指定する必要がある。具 TabIe 2 LASER SHOT 凵 PS 関数群⑤ 体的には , 以下のようにする。 関数名 area_pattern ( ) 関数 塗りつふしノヾターンの指定 /usr/5bin/cc sample. c -g -lcbox box( ) 関数 四角の描画 —lcurses ー 0 sample circle( ) 関数 円の描画 daen( ) 関数 楕円の描画 こて、、、一 lcbox 〃とは , ライプラリ libcbox. draw picture ( ) 関数 描画開始宣言 a の指定て、ある。 end picture( ) 関数 ピクチャ終了宣言 go_to_vector( ) 関数 テキストモードからべクタモードへの移行 labs( ) 関数 long 変数の絶対値の取得 ( IJN Ⅸ用 ) ・ UNIXcurses 用 config. h line kind( ) 関数 線種の指定 以下の内容の 00 。 f . h を作成し : 力ト line width( ) 関数 線幅の指定 myline( ) 関数 直線の描画 ディレクトリに入れておく必要力ある。 return tO text( ) 関数 べクタモードからテキストモードへの移行 #define UNIX ピクチャ開始宣言 start_picture( ) 関数 vecto 「 va 「 ( ) 関数 べクタモード用のノヾラメータへの変換 #define SYSV べクタモード用の座標単位の指定 zahyov unit( ) 関数 #define CURSES r にする / 漢字を使用する / 警告あり / ラー ~ [ 参考文献 ] ■ MS-C ジメモリモード / リンクは行わずコンバ [ 1 ] 内田勝巳 / 桑田愛子著 , 『最新 LASER イルのみ行う ) MS-C て、コンパイルする場合には , 従来と 同様にコンパイルスイッチを設定するとと SHOT 活用ブック』ソフトバンク [ 幻寺ロ俊伸著 , 『エーアイムック⑥レー ・ TurbO C 用 config. h もに , MS-C コンパイラのディレクトリ (M ザショットのすべて』ェーアイ出版 #define TURBOC (C) 下の INCLUDEi•ィレクトリの中に , c [ 3 ] TLASER SHOT プログラマーズマニ #ifndef MSDOS onfig. h として以下の内容のファイルを入れ #define MSDOS ておく必要がある。 ュアルコマンドリファレンス 3.1 』キャ #endif CL /AL /Od /W2 /Zi /J /c 関数 . c #ifndef PROTOTYPE ( ラージメモリモード /CodeView を使用 / [ 4 ] 『 LASER SHOT プログラマーズマニ 警告レベルは 2/char を unsigned char に #define PROTOTYPE ュアルクックプック 3.1 』キヤノン する / リンクは行わずコンバイルのみ行 [ 5 ] TLASER SHOT プログラマーズマニ う ) ュアルソフトウェア概説書』キヤノン Fig. 15 daen ( ) 関数の書式 daen(x,y, Ⅸ , Ⅳ , kaiten) , long x : long y , long rx , long Ⅳ , long kaiten , 関数の機能 #endif 132 C MAGAZINE 1992 9

3. 月刊 C MAGAZINE 1992年9月号

うるう ( 4 ) 閏年の計算は , 次のように行う。 うるう 西暦年が 4 て、割り切れる年は , 閏年て、ある。ただ し , 西暦年が 100 て、割り切れるが , 400 て、割り切 れない年は平年て、ある。 ( 5 ) 正弦曲線は , 61 桁の幅に印字する。 sin(x) X30 十 30.5 の値の小数部分を切り捨てて 整数値としたものを , 印字位置 ( 配列 ten ) とす ( 6 ) 印字する文字は , 肉体が ' # ' , 感情が ' 十 ' , 知性 が ' * ' て、ある。ただし , 肉体 , 感情および知性 のうちふたつまたは三つが重なったときは , ' X' を印字する。 ( 7 ) 印字例は , 図のとおりて、ある。 〔設間〕プログラム中の一を埋め 31 , 30, 31 , 30, int msu [ 12 ] ; は , 31 , 31 , 30, { 31 , 28 , int msu [ 12 ] まず , 各月の日数の表 のて、 , 少し解説しておきましよう まだ説明していないことをいくっか使っています プログラムの解説 て , プログラムを完成せよ。 31 , 30 , 31 } ; と宣言して msu [ 0 ] msu [ 1 ] msu [ 11 ] 28 ; と代入したのと同じことて、す。 char moji [ 3 ] Fig. A bio. c のコンノヾイルと実行 A>Icc bio. c -lmathlib@ biO. C 5 : Warning: macro lld @link.i A>bio@ 1942 1 0 31 1992 4 1 固 84 C MAGAZINE 1992 9 'PI' redefined ←コンノヾイル (LSI c ー 86 試食版 ) ←調べたい年月日を入力 ←生年月日を入力 ←起動 ←円を再定義したという警告は無視 践 C プログラミング も同様て、す。 数学関数 sin ( ) が使ってありますのて、 #include く math. h > bio. c をご覧ください て、きましたて、しようか ? 解答は付録ディスクの 全て、すが , 出題の関係上このようにしました。 のように引数の型も含めた宣言をしておくほうが安 int hi, int tsu [ ] ) ・ long keisan (int nen, int tsuki, in ( ) に入る前に のように戻り値の型だけ指定しています。本当は ma long keisan( ) ・ ろにありますのて、 , main ( ) 中て、 なお , keisan( ) がそれを呼び出す main ( ) より後 になります。 1 ) * 365L (nen となってしまいます。 (y) * 365L なら期待どおりに nen 1 * 365L として使ったときに NlSSUF(nen 365L のようにカッコをつけないと , 違い , 単なる字句の置き換えをするだけなのて、 , y * こうして # define て、定義したものは , 本当の関数と 算されます。 ん。どちらか一方て、も long 型なら , long 型として計 んし , (y) を (long) (y) とキャストしてもかまいませ りにキャストを使って (long) 365 としてもかまいませ て、は int の上限 32767 を超えてしまいます。 365L の代わ す。 (y) * 365 だけて、は , MS-DOS 上の C コンパイラ て、 365 に L がつけてあるのは , long 型にするためて、 っていませんのて、 , さして意味がありません。 現在の暦 ( グレゴリオ暦 ) がて、きてから数百年しかた は西暦 y 年まて、の日数の計算式の定義て、す。ただし , (y)/100 十 (y)/400) #define NISSUF (y) ( (y) * 365L 十 (y)/4 というエラーメッセージが出ます (Fig. A) 。 Undefined symbol: ん。これがないと , ン - lmathlib をつけてコンパイルしなければなりませ のように数学関数ライプラリをリンクするオプショ lcc bio. c -lmathlib が必要て、すし , 4 月号付録の LSI C ー 86 試食版て、は

4. 月刊 C MAGAZINE 1992年9月号

五ロ 用 応 C の道具箱 mybox( ) 関数が四角 , そして daen ( ) 関数が 楕円を描画する関数て、ある。これらの関数 の書式は , Fig. 13 , Fig. 14 , Fig. 15 のとお りて、ある。 3 個の関数の上にある area_patte rn ( ) 関数は , 図形内部の塗りつぶしパター ンを指定する関数て、ある。 CIRCLE3 を実行すると Fig. 16 のような図 形がプリントされる。 道具箱に追加された関数 LASER SHOT の 凵 PS 関数群 6(List 2 ) Table 2 に , LASER SHOT のページ記述 言語て、ある LIPS のコマンドを利用した関数 のうち , 今回追加したものを示す。 コンバイル 道具箱に追加された関数は , TurboCV er. 2.0 および MS-CVer. 6.0 上て、コンパイ ル可能なものて、ある ( UNIX 用のものは , S UN ワークステーションおよびその互換機て、 コンパイル可能のものて、ある ) 。いずれの場 合にも各プログラムは分割コンパイル用の ものなのて、 , コンパイル後オプジェクトフ ァイルを読者のライプラリに追加していだ 。なお , 1iPS9209. c ( 付録ディスク収 録 ) については , 前回まて、の lips 関数群をす べて含んて、いるのて、 , 前回の 1iPS9208. c はラ イプラリから削除していただきたい Turbo C Turbo C て、コンパイルする場合には , コ ンパイルスイッチを以下のように設定する とともに , Turbo C コンパイラのディレク トリ下の INCLUDEi•ィレクトリの中に , c onfig. h として以下の内容のファイルを入れ ておく必要がある。また統合環境の中て、コ ンパイルするのて、はなく , TCC を使用す tcc -v -K -J -w -ml -c ( デバッガを使用 /char を unsigned cha Fig. 6 90 to_vector( ) 関数の書式 go_to vectO 「 (genten) char * genten : ←座標原点の移動 座標原点を移動しない 現在印字位置 ( CAP ) を座標原点とする 1 Fig. 7 start_picture( ) 関数の書式 start_picture (label) ←画像の名称 , ただしコメントの意味しかない char *label,• Fig. 8 zahyov unit( ) 関数の書式 zahyov unit(unit, kakudai) ←基本単位 long unit : 1 / 720 インチ ー 1 1 / 100mm long kakudai : ←拡大率 Fig. 9 line width( ) 関数の書式 line width (width) ←線幅 long width 1 ドット幅 ー 1 3 ドット幅 ー 2 5 ドット幅 ー 3 7 ドット幅 ー 4 1 ~ 255 の任意の幅 1 以上の整数 Fig. 10 line kind( ) 関数の書式 line kind(kind, kakudai) ←線種 long kind , 実線 0 長鎖線 1 長点線 2 1 点鎖線 3 2 点鎖線 4 短鎖線 短点線 ユーサ線種ー 1 0 ~ long kakudai : ←ラインバターンの偏倍率 0 ~ 255 Fig. 1 1 線幅の異なる直線 0 12 Fig. 線種の異なる直線 応用 C 言語 131

5. 月刊 C MAGAZINE 1992年9月号

指定しない汎用のポインタ」という意味て す。なお , 単に void だけなら戻り値がない ことを意味します。 パラメータの size t は , メモリ領域のバイ ト数を指定するための型て、す。これは実は unsigned または unsigned long の別名て、 , s tdlib. h て、 , [ プログラム ] #include く stdio. h> #include く st ⅵ . h> #include く math. h 〉 #define PI 3.14159265 #define PI 30.0 #define 30.5 main() typedef unsigned size t , または , typedef unsigned long size t ; などと定義されているはずて、す。 具体的には , たとえば a [ 0 ] から a [ 99 ] まて、の 100 個の double 型の場所を確保するに 践 C プログラミング double *a ; 型になります ovoid * 型はキャストなして、 d * 型て、すが , a に代入されると double * のようにします。 malloc ( ) の戻り値は voi a=maIIoc(100*sizeof(double)) ; double * 型にも int * 型にも代入すること がてきます。 int m, n, nenl, nen2, tsukil' tsuki2' hil' i2 : long nissul, nissu2, sou, keisan() ; int msuC12] = { 31 , 28 , 31 , 30 , 31 , 30 , 31 , 31 , 30 , 31 , 30 , (1) , tsuC 亠 ] ; int shuki [ 3 ] = ( 23 , 28 , 33 } , tenC3] : char 亠 moj i [ 3 ] = dashes tsu[0] = 0 : = tsuCn-1J + u [ 止 1 ] ; for (n = 1 : n く = 12 ; 舳 + ) tsu[n] scanf("XdXdXd", &nenl, &tsukil, il ) : scanf ( ” % d ” , &nen2, &tsuki2, i2 ) : = keisan(nenl, tsukil, hil, tsu) ; nissul nissu2 = keisan(nen2, tsuki2, hi2, tsu) ; 3 sou printf " % 24d ー % 2d ー % 2d KARA NO BIO-RHYTHM CALENDAR%n ” , nen2' printf("X64d-X2d-X2d UMARE}n ” , nenl, tsukil, hil) : ” JOSHO-KI ” ); printf("X27sX13sX18s *n", "KAKO-KI" printf( ” Xs*n", dashes) : plot [ 61 ] = ' 判 ' : for (m = 0 : m く 30 : m + + ) { for (n = 0 ; れく 61 ; n + + ) plot[n] - for (n = 0 ; n く 3 ; n + + ) { ten Cn] = tsuki2, hi2) : sin((sou % shuki[n] + m) * ( 円 * 2.0 / shuki[n))) * 円 + : plot[ten[n]] = mojiCn]; if (tenC0] = tenC1] Ⅱ tenCOJ = tenC2]) plot[tenC0]] else if (tenCI] = ten[2]) plot[ten[l]] = printf("X4d ー % 61S は 3d \ n ” , m, plot. m) ; hosei = tonen ー zennen ー 365 : tonen = NISSUP nen) : zennen 6 long nissu, zennem hosei : 1 。 keisan( 亠 ) #define NISSUF(Y) ( ( y ) * 365L + (y)/4 ー ( の / 100 + ( y ) / 40 の return 0 ; printf(" Xs*n", dashes) ; return nissu; if (tsuki 〉 2 ) 亠 : nissu = zennen + tsu[tsuki - 1 ] + hi; 実践 c プログラミング入門 83

6. 月刊 C MAGAZINE 1992年9月号

のようにキャストて、型をそろえるべきて、す が , 値 0 については特例としてキャストを省 略て、きます。 List 1 て、 (unsigned char far * ) P ( (unsigned long) P 十 0X1000 の・ となっている箇所は , p のセグメント部を 1 増やしているだけて、す。て、すから , 番地と しては 16 バイト進みます。テキスト VRAM のような大切な領域の先頭番地はほば確実 に 16 の倍数て、しようから , 16 バイトごとに 調べるという手抜きをしているのて、す。 31 : } 2 ist テキスト VRAM の番地を調べる / * unsigned char far * に ucharptr という別名を与える * / typedef uns i gned char far * ucharptr : typedef unsigned 10 u10 : / * unsigned 10 に u10 という別名を与える * / 2 : #include く *. h 〉 1 : #include く stdio. h 〉 3 : 4 : 5 : 8 : 9 : 10 : 11 : 12 : 13 : 14 : 15 : 19 : 20 : 22 : 23 : 24 : 25 : 26 : 28 : 29 : 30 : 6 : main() if ()p ー do { p = (ucharptr) 0 ; printf("*x1bC2JA*n ” ) : ucharptr p; int i; / * ポインタ p を宣言 * / / * p を 0 番地に * / これを , (unsigned char far * ) P ((unsigned long)p 十 1 ) ; としたときどうなるか , 考えてください typedef と書いておきます。また , unsignedcharf typedef unsigned long ulong をつけるには , プログラムの頭に たとえば unsigned long に ulong という別名 のて、 , 短い別名をつけておくと便利て、す。 型の名前はけっこう長くなることが多い とします。こうしておけば , キャストも ucharptr typedef unsigned char far * ar * に ucharptr という別名をつけるには , ポインタによる呼び出し き換えたものて、す。 List 2 はこの typedef を使って List 1 を書 とて、きるのて、便利て、す。 (ucharptr) OxA0000000 ; の代わりに 0xA0000000 , (unsigned char far * ) void foo(int x) 側が変えることはて、きません。たとえば , C 言語て、は , 関数に渡した引数の値を関数 printf( ” }x1bC2JB%n") ; if ( 夘 printf("*x1b[2JVRAM: 0x % 081X \ n ” , p) ; for (i = 0 ; i く 16 ; i + + ) { printf("X081X: % 02X ” , p, (P) : if (isprint(*p)) printf( ” ' Xc' , *p); printf( ” %n"); p 十十 ; break ; printf("*x1bC2JA*n ” ) ; = (ucharptr) ( ( u10 ) p + 0X1000 の ; P return 0 ; ) whi le (p ! = の ; という関数 foo ( ) を main ( ) から f00 (x) ・ printf ("%d*n", x) ; 渡すだけなのて、 , 配列の内容を書き換える の先頭番地を別のポインタ変数に代入して 別の配列に代入して渡すのて、はなく , 配列 て、も , 引数が配列の場合は , 配列全体を の x は 5 のままて、す。 別物て、す。 f00 ( ) の x を 6 にしても , main() も , main ( ) の x と f00 ( ) の x とはまったく す。たまたま同じ x という名前がついていて ものて、はなく , x の値を代入した別の変数て、 はなく 5 て、す。 f00 ( ) に渡されるのは x その のように呼び出しても , 表示されるのは 6 て、 ことは自由にて、きます。たとえば , void foo(int x [ ] ) f00 (x) ・ しておきます。これを呼び出すには , すなわち int 型のものを指すポインタ x と指定 のように , 引数の受け皿としては int * x ; *x 十 1 ; void inc(int *x) ます。そのためには , たとえば 関数はその番地の内容を変えることがて、き 配列て、なくても , 関数に審地を渡せば , す。 のように呼び出すと , 表示されるのは 6 て、 printf ("%d*n", x [ 0 ] ) ; 十 1 ; i nc ( & x) ・ printf("%d*n", x) ; のように x て、はなく &x とします。 は , x の番地という意味て、す。 この & x x C0] という関数を 同様に , x と y の値を交換する関数は次の ようになります。 80 C MAGAZINE 1992 9

7. 月刊 C MAGAZINE 1992年9月号

サンプルプログラム サンプルプログラムとして直線を描画す る MYLINE3 を紹介する (List 1 ) 。 job start() 関数は , 前回も説明したジョ プ開始命令て、 , ここて、はコマンドレベルを LIPSIII モード , 解像度を 300dpi に設定して go to vector() 関数は , テキストモード からべクタモードへ移行するための関数て、 ある。この関数の書式は Fig. 6 のようになっ ている。 start_picture( ) 関数は , 描画パラ メータの設定の開始を宣言する関数て、 , 書 式は Fig. 7 のようになっている。 zahyov u nit ( ) 関数は , べクタモードにおける座標の 単位を設定する。前述のように この座標 単位は , テキストモード , べクタモードそ れぞれ別々に設定しなければならない。 za hyov unit( ) 関数は , Fig. 8 のような書式に int handle ・ 30 : #ifdef UNIX 28 : char data[150å : 27 : LONG xl, yl, x2 y2; 26 : LISTG listg ・ int i; 22 : void main() 20 : #endif wakprint(void) ; mojisets(void) ; void main(void); #include "cboxprot. h" #include ” Iipspr02. h ” 14 : #ifdef PROTOTYPE #include く curses. h> 11 : #include ” Iipsvar2. h ” 10 : #include "Iips2. h ” 9 : #include ” pld . h ” 7 : #include く string. h> #include く fcntl. h 〉 #include く stdio. h> #def i ne MYEXTERN ” conf i g. h ” #include 直線を描画する なっている。 MYLINE3 て、は 1 / 100mm を基 本単位とし , 拡大率を 1 に設定している。し たがって , べクタモードて、の座標単位は , 1 / 100mm ということになる。 draw_picture( ) 関数は , 印字パラメータ の初期化および座標単位の設定が終了した ことを宣言し , 具体的な描画手順の記述が 開始されることを示す関数て、ある。したが って , この後に直線や円を描画する関数を 置けばよいことになる。 myline( ) 関数は , 直線を描画する関数て、 ある。この関数は , 通常の line ( ) 関数と同様 の関数て、始点の座標および終点の座標を指 定するだけの単純な関数て、ある。ただし , 注意していただきたいのは , 座標の指定は long 定数て、行うという点て、ある。ところて、 実際に直線を引こうとする場合には , 線の 幅や線の種類を指定する必要がある。この 指定を行う関数が , line width() 関数と lin e kind( ) 関数て、ある。前者は線幅を指定す る関数 , 後者は実線 , 点線などの線種を指 定する関数て、 , それぞれ Fig. 9 , Fig. 10 の ような書式になっている。 end_picture( ) 関数は , 描画の終了を宣言 する関数て、ある。この関数は , start_pictu re ( ) 関数と対になっているものて、 , これら のふたつの関数にはさまれた描画指定のプ ロックをひとつの単位としてヒ。クチャと呼 んて、いる。したがって , start_picture() 関 数は 1 ビクチャの開始宣言用の関数 , end_p icture ( ) 関数は終了宣言用の関数と言い換 えることもて、きる。 return to text ( ) 関数 は , べクタモードからテキストモードに復 帰するための関数て、ある。 MYLINE3 を実行するとさまざまな直線が 描画される (Fig. 11 , Fig. 12 ) 。 次に円 , 四角 , 楕円を描画するサンプル プログラム CIRCLE3 を紹介する ( 付録ディス ク収録 CIRCLE3. TST 参照 ) 。 circle ( ) 関数が円を描画する関数て、あり , 1 : 2 : 3 : 4 : 5 : 6 : 8 : 12 : 13 : 15 : 16 : 17 : 18 : 19 : 24 : 25 : 29 : 31 : 32 : 33 : 34 、 35 : 36 : 38 : 39 : 40 : 41 : 43 : 44 : List 1 if((handle = open("/dev/ttyb", O_WRONLY ) ) ーー 1 ) { printf("can' t open printer }n") ; exit(l) ; if((stdprn = fdopen(handle, "w") ) = NÜLL){ printf( ” can' t fopen printer }n ” ) : exit(l); system("/usr/5bin/sttY raw く /dev/ttyb") : 42 : #endif Gdpi = 300 : 130 C MAGAZINE 1992 9 List 1 45 : 47 : 50 : 52 : 53 : 54 : 55 : 56 : 58 : 59 : 61 : 64 : 66 : 68 : 69 : 70 : 77 : 82 : 83 : 46 : #ifdef MSDOS for(i=l ; i く 60 : i + + ) { = 1000L ; x2 ニ 20000L ; YI = 1000L ・ xl = 1000L ; line—kind(0L, (L) ; draw—picture() : zahyov—unit(MM, 1L5 ; start—picture( ”” ) ・ go—to_vector( ” 0 ” ) ; zahyot-unit( ” ?6 ” ) ; size-mode() : initlaza() : 51 : #endif job—start( ” 31", ” 300 ” , 49 : #ifdef UNIX 48 : #endif job ー s ね rt ( ” 31 ” , ” 300 " , ” 4 " ) : exit( の ; job-end() ; kaipage(); return_to_text(&p) : end—picture(); myline ( xl , YI + 500 * i , x2 , y2 + 500 * i ) ; line-kind((LONG)(i % 7 ー 2 ) , 0L ) ; for(i=0 ; i く 60 ; i + + ) { Iine-width(30L); kaipage(); line ( xl , YI + 500 * i , x2 , y2 + 500 * i ) ; Iine_width((LONG)i*4);

8. 月刊 C MAGAZINE 1992年9月号

任意の型の大きな配列を EMS から確保するクラス / / EMSMEM ー EMS メモリを使って大規模な配列を実現するクラス #include く stdio. h> #include く dos. h> const int EMSVECT = 0X67 : const int EMSPAGESIZE = 0X4000 : / / EmsBase: / / EMS のべースセグメントに関するクラス / / 各型に対する EmsMem クラスから共通にわれます。 class EmsBase { public: static unsigned Base; static unsigned getEmsBase(void) ; / / EmsMem : / / EMS を使って仮想的に大きなメモリを割り当てるクラス。 template く class T> class EmsMem { private: int handle; / / EMS ハンドル 10 num; / / 要素数 public: EmsMem(long n) ; EmsMem() ; int getHandle(void) ( return handle; } T far& operator [ ] ( 10 index) ; / / EMS ペースセグメント getEmsBase() によって初期化しておきます。 uns igned EmsBase: : Base = EmsBase: :getEmsBase() : / / EMS ペースセグメントの取得 EmsBase: :getEmsBase(void) struct REGPACK regs ; regs. r—ax = 0X4000 ; if 。。 g 、。ー。 , 〉 > 8 トの / / Error! return 0 ; regs. r—ax = 0X4100 : if ( 80g 、。ー 0 、 > > 8 ! = の / / Error! return 0 ; return regs. r_bx; Lsit 2 Lsit 2 、 10 00 4 LO へ 0 ー 8 0 》 0 ・ 1 りム ^ 0 ー 8 01 《り介 0 4 ・′ 0 ー 8 9 01 りっ 0 4 -0 ^ 0 ー 8 0 リ 01 ・ワ 00 4 : 0 8 9 01 り 00 -4 【 0 6 ー 8 01 よりん 00 4 【 0 1 ー 8 0110 っ 0 -4 ニ 0 ^ 0 ー 8 0 》 0 、 1 っ 0 っリ -4 ・ -0 ・ 1 ・ 1 ・ 11 上 1 よ 1 よ・ー -1 人 1 よ・ 1 っ 0 つなつなつな 0 乙り 0 んりりっ 00 っ 0 00 れ 0 0 っ 0 00 れ 0 00 っ 0 4-4-4 4- 4- -4 -4-4 4-4 ′ 0 0 ′ 0 ′ 0 -0 【 0 - -0 ′ 0 戸 0 ′ 0 ^ 0 れ 0 れ 0 6 ^ 0 れ 0 れ 0 7 ーワー叮ー叮ーっーっーっーっー「ー 8 8 8 8 8 8 〃 EMS メモリの残リ容量が不足していれば工ラー regs. r-ax = 0X4200 ; re turn : if ((long)regs. r_bx * EMSPAGESIZE く size) re turn : 〃 EMS メモリの確保 regs. r—ax = 0X4300 : regs. r_bx : (unsigned) (size / EMSPAGESIZE) : handle ニ regs. r—dx; 86 : 88 : 89 : 90 : 93 : 94 : 95 : 96 : 98 : 99 : 100 : 101 : 102 : 103 : 104 : 105 : 106 : 107 : 108 : 109 : 110 : 111 : 112 : 113 : 114 : 115 : 116 : 117 : 118 : 119 : 120 : 121 : 122 : 123 : 124 : 125 : 126 : 127 : 128 : 129 : 130 : 131 : 132 : 133 : 134 : 135 : 136 : 137 : 138 : 139 : 140 : 141 : 142 : 143 : 144 : 145 : 146 : 147 : 148 : 149 : 150 : 151 : 152 : 153 : 154 : 155 : 156 : 157 : 158 : 159 : 160 : 161 : 162 : 163 : 〃 EmsMem クラスのデストラクタ / / 割リ当てた EMS ハンドルを解放します。 template く class T> EmsMem く T>: :¯EmsMem() struct REGPACK re s, if (handle トの ? regs. r—ax = 0X4500 ; regs. r_dx = handle; intr(EMSVECT, &regs) ; / / EmsMem クラスの添字演算子 ( ロ ) メンバ 配列のアクセスと同様に扱えるようにするため、 / / 添字演算子を定義しています。 ※注意ある要素のポインタをとると far ポインタになリます。 しかし、そのポインタが指す内容は、別の要素が 参照されることで無効になります。ポインタによる 間接アクセスはしないようにしてください。 ※補足変数を参照するたびにページ # 0 への割り当てを 行なっていますので、処理速度は遅くなります。 template く class T> T far& EmsMem く T>: :operator[](long index) struct REGPACK regs : / / EMS ハンドルのマップ 〃 physical = # 0 ニ 0X44 圓 ; regs. r_ax regs. r-bx = index / EMSPAGESIZE; regs. r dx : handle; intr(EMSVECT, &regs); 〃工ラーチェックはしない return *(T far *)MK_FP(EmsBase: : Base, (unsigned) (index % EMSPAGESIZE)) : / / EmsMem クラスのコンストラクタ / / 必ず、引数として要素数を指定します。 / / 型としては、 2 のべき乗のサイズを持つ型のみが使えます。 / / 型としてクラスを指定することはできません。 ( コンストラクタが呼び出されません ) / / EMS の確保に成功したかどうかは getHandle で調べられます。 template く class T> EmsMem く T>: :EmsMem(long n) : num(n) struct REGPACK regs : 10 size ニ n * sizeof (T) ; hand le = 0 ; 〃ペースセグメントが取得できていなければ工ラー if (EmsBase: :Base = の return; / / 型のサイズが 2 のべき乗でなければ工ラー ニ sizeof (T); (s & 1 ) for (size_t s if (s ! : 1 ) return; 〃テストプログラム main() EmsMem く 10ng〉Ⅷ ( 0X4000 の ; 10 i ; if (Ⅷ . getHandIe() = の { puts("can' t allocate EMS memory") : return 1 : for (i = 0 : i く 0X40000 : i + = 0X100 の vm[i] = for (i = 0 : i く 0X40000 : i + = 0X100 の printf("vm[Xld] = Xld#n", i, vmCi]) : return 0 ; lnformation from Compiler Makers 163

9. 月刊 C MAGAZINE 1992年9月号

ln れ川 a ⅱ行匪 m 町ⅱ計 Ma れ門 ボーランド BorIand C 十十 Ver. 3.0 今回は , Borland C 十十 Ver. 3.0 てサポートされた template につ いて , どのような使い方がてきる のかを説明いたします。 template は , 型や値だけが異な るクラスや関数をひとつにまとめ てしまうものてす。 template につ いて , 最大値をとる手法を例に考 えてみましよう。 マクロ機能を利用 C の場合ては最大値を得るプログ ラムは Fig. 1 のようなマクロて、実現 します。 こうすることて、 , ふたつの引数 の型が同じてあれば型の最大値を 得ることがてきます。しかし , の方法てはマクロはプリプロセッ サによって置き換えられるだけて、 すから , 引数の型が違ったり意味 のない識別子を渡してもプリプロ セッサは , 文法工ラーを発生しま せん。 C コンパイラは , 置き換えら れた内容をコンパイルしてから問 いてしよう (Fig. 2 ) ) 。 これは , 通常の関数と同じよう にコンパイラ自身が認識します。 したがって , コンパイラは間違っ た引数に対して警告やエラーを表 示てきますし , デバッグの際にも max という名前を参照することが て、きます。オーバロードによって , 危険なマクロよりも安全なプログ ラミングが可能になりました。 template 機能 こて、定義した関数を見 さて , てみるとそれぞれ引数や戻り値の 型が違うだけて、 , 関数の本体はま ったく同じものてす。このように 型が違って処理内容が同じものは , template を使ってひとつにまとめ プログラムをすっきりと記述する ことがて、きます。 template を使う と , 最大値を求める関数は Fig. 3 の ようになります。 Fig. 3 て、 , class T とは「 T という 型」 ( 任意の型 ) に置き換え可能なこ とを意味します。つまり , T max (T a, Tb ) は任意の型に適用て、き ますから List 1 のようなプログラミ ングがて、きます。 整数の最大値 ( max ( 1 , 9 ) ) , 実 数の最大値 ( max ( 98.7 , 32.1)) を 求めるための関数は , 必要になっ た時点て、コンパイラが自動的に生 成します。 template の定義が複数 のソースファイルにまたがると , ソースファイルごとに関数定義が て、きあがってしまうことになりま すが , BC 十十ては同じ型に対する ものは仮想セグメントを使ってリ ンカがひとつの定義にまとめます。 また , 関数の直前に inline と記述す ればインライン関数として定義す ることもてきます。 最初に述べたとおり , template はクラスに対しても使えます。ク ラス定義の前に template 宣言を追 加しておき , 関数の場合と同じよ Fig. 1 最大値をとるためのマクロ うにクラスの中て型や値を使うだ けて、す。サンプルとして任意の型 の大きな配列を EMS から確保する クラスを , template を使って実現 した例を List 2 に掲載します。 Fig. 2 オーバロード機能を使用した型ことの関数 int max(int a, int b) 「 eturn (a > b) ? a long max(long a, long b) return (a > b) ? a double max(double a, double b) 「 eturn (a > b) ? a Fig. 3 template を使用した max() 関数 return (a > b) ? a Tmax(Ta, (b) { template く class T> 題があることを指摘します。また , プリプロセッサはマクロ定義され たシンポルを置き換えて隠してし まいますから , デバッグの際にも マクロ定義されたシンポル名を参 照することがてきません。このよ うに , Fig. 1 のようなマクロによる 方法ては , プログラムにバグがあ った場合その発見が極めて困難に なることがあります。 オーバロード機能の使用 C 十十ては , オーバロードという 機能を使って型ごとに別々の関数 を用意することがてきます ( 実際に はインラインて定義することが多 162 C MAGAZINE 1 2 9 Lsit 1 : 2 : 4 : 8 : 9 : 1 最大値を求める関数の使用例 #include く iostream. h> template く class T> 3 : T max(T a, T b) ( return (a 〉 b) ? a : b; 6 : void min() ”くく x ( 1 , 9 ) くく e 1 : ・くく max ( 98.7 , 32.1 ) くく endl; cout くく” max 98.7. 32.1 ) = cout くく” max 1 , 9 ) 10 : )

10. 月刊 C MAGAZINE 1992年9月号

却プログラミング Fig. 2 do—while Fig. 1 セグメントとオフセット セグメント オフセット 0 0 0 A 0 0 0 0 A 0 0 0 十 0 0 0 0 ループ本体 do { ループ本体 } while ( 条件 ) ; 条件 成立 不成立 A 0 0 0 0 とえばセグメントの値が 0X1234 , オフセッ 体的には , トの値が 0X5678 て、あれば , char far *p 0XA0000000 ; P 0X1234 0X41 ; 十 0x 5678 とすれば画面の左上隅に A が現れます 00xA 0X179B8 } while ( 条件 ) ; 0000 番地を指定するには 0XA0000000 とする という番地を指定したことになります。同 理由は先ほど述べたとおりて、す。 は , 条件が成立しなくなるまて、「・・・・司の部 テキスト VRAM の番地は機種によって異 じ番地を指定するためのセグメント・オフ 分を繰り返すという命令て、す ( Fig. 2 ) 。 なります ( 固定したテキスト VRAM を持って セットの組み合わせはたくさんあります。 比較 p!= 0 は本来なら , いない機種もあります ) 。 List 1 はテキスト 8086 のこのような特性を生かすために (unsigned char far * ) 0 MS-DOS 用の多くの C 言語て、は , テキスト V 日 AM の番地を調べる (unsigned char far * ) 0X12345678 ; としたとき , p のセグメントが 0X1234 , オフ セットが 0X5678 になるように作られていま す。て、すから , 実際の番地は 0X179B8 て、す。 VRAM の先頭番地を調べるプログラムて、 す。 List 1 て、 do { List P 1 1 : #include く stdio. h> 2 : #include く ctYFE. h> 3 : main() int i; 5 : / * ポインタ p を宣言 * / 6 : uns igned char far *P ; 7 : printf( ” *x1bC2JA%n") : 8 : / * 画面消去 , A を表示 * / P ニ (unsigned char far * ) 0 : / * p を 0 番地に * / 9 : do { 10 : / * 繰返し * / if ()P ー / * p 番地の内容が ' A ' なら * / 12 : printf("%x1bC2JB*n ” ) ; / * 画面消去 , B を表示 * / if (*P 13 : / * p 番地の内容が ' B ' なら * / 14 : printf( ” *x1b[2JVRAM: 0x % 08 lX*n ” , p) ; 15 : / * 画面消去 , 駅 : ox???????? のように表示 * / for (i = 0 : i く 16 ; i + + ) { 16 : printf("X081X: % 02X " , p, (p) ; 17 : 18 : if (isprint(*p)) printf(" ' Xc' " , (p) ; 19 : 20 : / * 夘が文字として表示可能なら表示 * / printf( ” *n ” ): / * 改行 * / 21 : 22 : / * p に 1 を加える ( p = p + 1 と同義 ) * / 23 : 24 : break ; / * ループ脱出 * / 25 : printf("Yx1bC2JA*n") : / * 画面消去 , A を表示 * / 26 : P = (uns igned char far * ) ((unsigned long)p + 0X1000 の ; 28 : 29 : / * p を unsigned long 型に変換して 0X10000 を加え , 30 : unsigned char far * 型に戻す * / 31 : / * p が 0 になるまで繰り返す * / 32 : / * 正常終了 * / 33 : ) テキスト V 日 AM の 番地を調べる printf("A") ; とすると画面に A の文字が 表示されますが , コンヒ。ュータ内部て、は , 次の 2 段階に分けて文字表示をします。 ① A の文字コード 0X41 をテキスト VRAM (ビデオ RAM) というメモリの特定の番 地に書き込む ②これを A という文字の形の点の集まりに 変換して , 画面に表示する このテキスト VRAM が PC ー 9801 て、は 0xA 0000 番地から始まります。て、すから , PC-9 801 て、 0XA0000 番地に 0X41 を書き込むと , 画 面の左上隅に A という文字が現れます。具 }while (p ! = の ; return 0 ; 実践 c プログラミング入門 79