struct - みる会図書館


検索対象: 月刊 C MAGAZINE 1991年6月号
20件見つかりました。

1. 月刊 C MAGAZINE 1991年6月号

アルゴリズム・テータ構造入門 1 : 2 : 3 : 4 : 6 : 9 : 12 : 13 : 14 : 16 : 17 : 18 : 19 : 20 : 22 : 23 : 24 : 25 : 26 : 27 : 28 : 29 : 30 : 31 : 32 : 33 : 34 : 35 : 36 : 37 : 38 : 39 : 40 : 41 : 42 : 44 : 46 : 47 : 49 : 50 : 52 : 53 : 54 : 55 : 56 : 58 : 59 : 60 : 61 : 62 : 64 : 65 : 66 : 68 : 69 : 70 : 71 : 73 : List 2 10 : { 43 : } 48 : { / * リストのセル * / struct node { i nt va I ue : struct node / * 整列のキーとなる値 * / / * 次のセルへのポインタ * / *next; ① ② 各要素を作業用の配列にいったんコピ ーしてから , もとの配列に向かってマ ージする必要がある。このコピーの手 間がバカにならない 作業用の領域として , 整列するデータ と同じ大きさの領域が必要になる 7 : / * 2 つのリスト a と b をマージする。 マージされたリストの先頭要素へのポインタを返す * / struct node *merge_l ist(struct node *a, struct node *b) struct node head, *p; / * ポインタ p がダミーの要素を指すようにしておく * / &head; P / * リスト a, b のいずれかが空になるまで繰り返す * / w h i 1 e (a ! = N U し L & & b い NULL) { / * リスト a , b の先頭の要素を比較する * / if (a->value ← b->value) { / * リスト a の先頭の要素を取り除いて , マージずみリストの末尾に連結する * / p->next a—>next; a } e lse { / * リスト b の先頭の要素を取り除いて , マージずみリストの末尾に連結する * / b = b->next; p->next / * 残っている要素をマージ済みリストの最後尾に連結する * / e ー se p->next = NU しい p- 〉 next ニ a; / * マージずみのリストを関数値として返す * / return head. next; 45 : / * リスト版のマージソート リスト x を整列する。整列されたリスト・の先頭要素へのポインタを返す * / struct node *merge_sort I ist(struct node *x) struct node *a, *b, *p; / * ポインタ b がリストの末尾に到達するまで , ポインタ a を 1 つ進め , b->next; if (b ! = NU しし ) x—>next; / * b は 3 番目の要素 ( もしリストの長さが 2 のときは 2 番目の要素 ) を指す * / / * a は 1 番目の要素を指す * / / * リストをスキャンするポインタを初期化する * / return x; = NU し L Ⅱ x->next ニ NU しい リストに要素がまったくないか , 1 つしかないときはそのままリターンする * / ポインタ b を 2 つ進める。ポインタ b が末尾に到達したとき , ポインタ a は , リストのほぼ中央の要素を指しているはずである * / b = b->next; if (b ! = NU しい b->next; a a—>next; while (b ! = NU しい { リスト構造を使えば , この二つの欠点を うまく回避することがて、きます。リスト構 造て、は , ポインタを書き換えるだけて、要素 を別の列に移すことがて、きます。つまり , わざわざ要素全体をコヒ。ーする必要もあり ませんし , そのために作業用の領域を必要 とすることもないわけて、す。 よく考えてみれば , リストて、は一つ一つ のセルが次のセルへのポインタをもってい るわけて、 , 作業用としてやはり O ( n ) の領域が 必要となる事実に変わりはありません。し かし , リスト構造の場合には , セルが本来 もっているポインタをそのまま流用て、きる のて、 , 見かけ上余分な領域を使わずにマー ジソートがて、きます。 マージソートの基本となるマージは , 「二 つの列の先頭から順番に要素を取り出して , それを出力側の列に連結していく」という操 作て、すが , これは列をシーケンシャルアク セスしていることにほかなりません。マー ジソートはもともとリスト構造に向いたア ルゴリズムなのて、す。また , つねに列を一 方向に向かって処理するのて、 , セルは次の 要素へのポインタさえもっていれば十分て、 す ( つまり双方向リストて、なくてもかまいま せん ) 。 List 2 がリスト構造を使ったマージソート て、のプログラムて、す。 2 ~ 5 行目て、セルを表 す構造体 struct node 型を定義しています。 セルは , 整列に使われる int 型の値 value と次 のセルへのポインタ next をもっています。 例によってメンバ next に NULL を入れて , リストの終わりを表すことにします。 関数 merge list は , 二つのリスト a , b をマ ージして , マージされたリストへのポイン タを返します。この関数は , Fig. 1 のアルゴ リズムをそのままコーディングしたものて アルゴリズムとデータ構造入門 63

2. 月刊 C MAGAZINE 1991年6月号

LiSt Program Maintenance UtilitY(pmk. c) 147 : / * 2 : 3 : 4 : 6 : 7 : 9 : 16 : 20 : 21 : 23 : 25 : 28 : 33 : 36 : 37 : 40 : 44 : 45 : 48 : 49 : 50 : 52 : 53 : 54 : 55 : 56 : 58 : 59 : 60 : 61 : 62 : } 63 : 65 : 68 : 69 : 70 : 72 : 73 : } 74 : 1 : / * pmk. c 5 : #ifdef #pragma #pragma 8 : #endif 10 : #include く stdio. h> 11 : #include く stdlib. h> 12 : #include く string. h> 13 : #include く malloc. h> 14 : #include 15 : #include く dos. h> Program Ma i ntenance Ut i ⅱ ty. Copyright 1991 by Yasunori Fuzi i. C 叩 yleft 1991 for Project pragma C. Ⅲ STORY REVISED "yfz, 08 ー ap に 91 , base for Computer lnnovatioin, C86P し US. " REVISED "yfz, 12 ー ap に 91 , touch to Microsoft 6.0 and Quick 2.0. " く process. h> 76 : 78 : 79 : 80 : 82 : 83 : 84 : 85 : 86 : 87 : 88 : 89 : 90 : 92 : 93 : 94 : 95 : 96 : 97 : 98 : 99 : 100 : 101 : 102 : 103 : 104 List 1 75 : / * オプションフラグの作成 * / int makeflags( int argc, char *argv[] ) int argn; char CC, *CP, *sp : memset( P し AGS, 0 , MAXF し AGS ) : for( argn = 1 : argn く argc; argn 十十 ) 17 : #def i ne MAXF し AGS 128 18 : static char F し AGS[ MAXFLAGS ] : 19 : static char *F し AGSMEMBER="hv" ・ int VERBOSE ニ 0 : 22 : / * PMK. C 内関数プロトタイプ int main( int, char * * ) : 24 : void usage( void ) : int makeflags( int, char * * ) : 26 : VOid error( Char * ) : 27 : void error2( char * , char * ) : int loadmakefile( char * ) : 34 : Char * convert_macro( Char * , Char int expand_macro( char * , char * ) : 32 : VOid set_command( char * ) : 31 : VOid set macro( char * , Char * ) : 30 : void set_target( char * , char * ) : 29 : void set_target-or-macro( char * ) : i nt argn : 47 : char makef ⅱ ename [ 80 ] : int main( int argc, char *argv[] 43 : char * findcommandfile( char * ) : 42 : char * serchfile( char も char * ) : 41 : char * serchpath( char * ) : int execute( char * ) : 39 : void update( int ) : 38 : unsigned long getfiletod( char * int istargetfile( char * ) : int maketargetfile( int ) : 35 : void makefiles( void ) : / * オプションフラグステイタス * / / * 有効オプションフラグ文字 / * V ERBOSE MODE ON/OFF F し AG cp ニ argv[ argn ] : e ー se F し AGS [ cc ] while( cc if( *cp ー if( ! *sp ) return -1 : for( sp ニ F し AGSMEMBER; *sp ! = cc; sp + + ) return 0 : return argn; 105 : 107 : 108 : 109 : 110 : 112 : 113 : 114 : 115 : 116 : 118 : 119 : 121 : 123 : 124 : 125 : exit( ー 1 ) : fprintf( stderr. "YnY7ERROR: %sYn" VO i d error ( char * message ) 106 : / * ェラーメッセージを表示して EX は message ) : VOid error2( char * messagel, char *message2 ) fprintf( stderr, " YnY7ERROR : %s %sYn" , message 1 , exit( ー 1 ) : message2 ) : 120 : #def i ne MAXTARGET 122 : struct _makelist Char *name : 64 fputs( "YnProgram MaKe uti lity version 1. 0Yn" stderr fputs( "CopyIeft 1991 by ProjectPragmaC. Yn", stderr ) : return 0 : makef ⅱ es ( ) : printf( ” \ n ” ) : loadmakefile( makefilename ) : i f ( argn ) strcpy ( makef ⅱ ename. if( F し AGS[ 'v' ] ) VERBOSE if( ( argn く 0 ) Ⅱ ( FLAGS[ 'h' ] ) ) usage(): argn = makeflags( argc, argv ) : strcpy( makefilename, "MAKEFILE" ) : 126 : 127 : 131 : 132 : 133 : 134 : 136 : 137 : 138 : 139 : 140 : 141 : 143 : 145 : 146 : char *line; Char *name; struct macrolist 135 : #define MAXMACRO struct make ⅱ st *next : 128 : struct makelist TARGET[ MAXTARGET ] : 129 : struct _makelist COMMAND[ MAXTARGET ] : 130 : struct _makelist *COMMANDLINK; i nt TARGETCOUNT : 0 : argv [ argn ] ) : 128 64 : / * 解説 VOid usage( VOid ) fputs( "Usage: PMK [-options] [makefile]Yn", stderr ) : fputs ( " fputs ( " fputs( "Yn" ex i t ( 1 ) : C MAGAZINE fputs( "Yn -optionsYnYn" stderr ) : V verbose on.. h he ゆ .. .. 実行情報を表示 \ n " この説明を表示 Yn ". stderr ) : stderr ) : stderr ) : 1991 6 142 : struct _macrolist MACRO[ MAXMACRO ] : 150 : FILE *fp; int loadmakefile( char *makefilename ) MAKEFI し E をロードする * / 144 : int MACROCOUNT ニ 0 : 149 : 148 : 54

3. 月刊 C MAGAZINE 1991年6月号

新 MS ー DOS プロクラミンク入門い C プログラマのための コラム コンヾイラのバクベの対処法 今回から本連載のプログラムに対応する 最適化のバグ ライプラリのバグは , ①等価のライプラリを自作してコンバイ 処理系に MS ー C Ver. 6.0 と Turbo C 十十 純然たる生成コードのバグ Ver. 1.0 を加えました。そこて、コンパイラ となるて、しよう。まず , 最初にすべきこと ラのライプラリよりも先にリンクする のバグ対処法を簡単に説明しましよう。 は , どこにバグがあるのかを突き止めるこ か , コンバイラのライプラリと差し換 コンパイラに限らずソフトウェアにとっ とて、す。当然 , 真っ先に疑うのは自分のプ てバグは避けることはて、きません。したが ログラムて、す。バグの原因を突き止めるに ②バグのあるライプラリを使用しないよ って , バグとうまくつき合うことも必要て、 は数秒 , 数分から数日とかなりの開きがあ うにプログラムを変更する す。コンパイラのバグは大別すると , ります ( 今回は誌面の都合上 , バグの突き止 などの対策が考えられます。 ライプラリのバグ め方は省略します ) 。 ①はライプラリのソースが手元にあれば 簡単て、すが , なければ仕様を合わせて自作 パイブッ″のバグ ( MS ー C Ver. 6.00A ) しなくてはいけません。雑誌の特集て、ライ プラリのソースを掲載したものがよくあり ますから , そのような記事を参考にすると よいてしよう。 ②はたとえば , fseek 関数にバグがあれば seek 関数を使用してプログラムを書き直し てみるといった方法て、す。 最適化のバグはどの最適化を行うとバグ が出るかを突き止めて , コンパイルスイツ チて、その最適化を抑制するのがもっとも簡 単な方法てす。 生成コードのバグはもっとも厄介なバグ て、す。これはコーディングを変えて試行錯 誤するしかないて、しよう。 List 3 の funcl は MS-C Ver. 6.00A のバ グて、 , unsigned< ーを使うと : を忘れ てとんてもないコードを生成してしまうと いうものてす。これは func2 のように を十 に変更するか , func3 のように unsigned を unsigned char に変えることにより対処てき ます。 List 4 の funcl は Turbo C 十十 Ver. 1.01 のビットフィールドに関するバグてす。 8 ビットのビットフィールドは正常のようて す。したがって , func2 のように 8 ビットのビ ットフィールドに変更するか , func3 のよう にビットフィールドを使用しないことて対 LlSt 1 : unsigned Char hour; 2 : unsigned far *pw 3 : 4 : void funcl( void ) ( hour % 10 ) ー 6 : 8 : 9 : void func2( void ) ( hour % 10 13 : 14 : unsigned char far *PC 15 : 16 : void func3( void ) pc[ 0 ] ニ 18 : pc[ 1 ] ニ 19 : 20 : } ( unsigned far * ) 0Xa0000000 し : / * 生成コート・が異常 *PW / * 正常 * / *PW = ( unsigned char far * ) 0Xa0000000 し : ( uns i gned char ) ( ( hour % 10 ) + ・ 0 ・ ) : / * 正常 * / ピットフィールドのバグ ( Turb0 C 十十 Ver. 1.01 ) LlSt 1 : struct { unsigned bitO 2 : 3 : uns igned unsigned bit8 4 : 5 : unsigned 6 : } sbitsl; 7 : 8 : void funcl( void ) 10 : sb i ts 1. b i t0 sb i ts 1. b i t8 13 : 14 : struct { struct { unsigned b i t0 17 : unsigned } s 0 : struct { 20 : b i t8 uns igned 、 1 ー 1 よ 0 ー 新 MS-DOS プログラミング入門 79

4. 月刊 C MAGAZINE 1991年6月号

ワンポイント プログラミング 講座 List 1 List 2 (*date). tm_mday 1 13 : 114 : } 115 : / * - 116 : / * 休日判定 ( 0 : 休日でない 1 : 指定休日 117 : / * 1 18 : / * 2 : 祝祭日 & 振替休日 119 : / * ー holiday(struct tn *date) 120 : i nt 121 ・ 122 : int day, i, J; 123 : FILE *fp; 124 : if (_horiday-table-read 125 : if((fp = fopen("horiday. tbl" 126 : for (i i 十十 ) 127 : i く 20 & & j = fscanf(fp, "Xd", &(-hoIytb12[i])); 128 : J 129 : fclose(fp) : 130 : 131 : 132 : horiday-table-read : YES; 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 : / * 日数計算 long daycount (struct tn * f 「 om. struct tm *tO) 161 : 162 : 163 : long days; 164 : -mjd(to) -mjd(from) : 165 : days if (days > = の { 166 : return(days 十 1 い : 167 : 168 : 169 : return(days 170 : } printf( ” i くニ dd; i + + ) { ( i for date. tm_mday holiday(&date) : if (k ! = の printf("YxIb[31n"); : の printf("Yxlb[31m") : else if (j = 6 ) printf("Yxlb[34m") : else if (j printf("X3dYxlb[0m" = の putchar('Yn ・ ) : -4- -0 《 0 0 ー 8 0 ・ 1 つむつ 0 4 ・戸 0 7 ー っ 0 06 っ 0 っ 3 りもワ CO っ 0 っっっ putchar( ・ Yn ・ ) : List 3 calen. C (*date) . tm_mon + 1 : i * 100 + (*date). tm_mday; day (i for 0 : i く number_of_horidays & & day if (day -holytbll[i]) return(2) : ( i 0 : i く number of_horidays & & day for if (day _holytb12[i]) return(l); -mjd(date) : if ((*date). tm_wday - if ((*date) . tm_mday ! day - 引 se { (*date). tm_mon; i * 100 + _mday[i-l]; d ay for ( i : 0 : i く number of-horidays & & day i f (day -holytbll[i]) return(2) : 1 : # i ncl ude く std i 0. h> 2 : # i nc lude く ca l. h > yesterday. tOda y : 6 : ー ong 7 : struct tm date, d2; 8 : 9 : 1 ヶ月分のチェック 13 : void check(int m) 15 : int 17 : date. tm_mon mday [ 田 -1 ] : 18 : for (i 19 : 20 : date. tm-mday mjd(&date) : 21 : tOday if (today yesterday ! = 22 : printf("X4d/X02d/X02d:mjd ニ % 届 \ n ". 23 : 1900 + date. tn_year, date. t 第 - 第 on + し date. tm_mday. tOday) : 24 : 25 : 26 : yeste rday tOday : 27 : -jdate(today. &d2) : 28 : if ((date. tm_year ! = d2. tm-year)&&(date. t 田一田 on ! = d2. tm-mon) 29 : & & (date. tm_nday ! ニ d2. tn-mday)) { 30 : printf("mjd : % 届 : % 4d / % 02d / % 02d : % d \ n " today, 1900 十 date. tm-year, 31 : date. tm-non + date. tm-mday, d2. tm-year) : 32 : 33 : 34 : 36 : 37 : / * 38 : / * チェックを 1 年分繰り返す 39 : / * - 40 : VOid year(int y) 42 : 43 : 44 : date. tm_year - for (m 45 : check 価 ) : 46 : 48 : } 50 : / * ~ 51 : / * テスト : メイン関数 52 : / * ー 53 : void main(void) 54 : { 55 : int y; 56 : / * 1900 年 * / 0 : date. tn-year = 1 月 * / 58 : 0 : date. t 第 - 第 on 59 : 1 日 * / date. tn-nday njd(&date) - 1 : / * の前日 * / yes terday printf(" チェック開始 Yn"); 62 : fo 「 (y : 0 : y く 200 : y + + ) { / 事 1900 - 2100 年 * / 63 : year(y) : 64 : 65 : 66 : p 「 intf ( ”チェック終了 \ n つ : -holytbll[i] : i 十十 ) -hoIytb12[i] : i 十十 ) / * 振替休日 ニ l) { / * 前日を計算 * / i 十十 ) -holytbll[i]; return(N0) : check. c List 2 = 12 : 十十 ) : #include く stdio. h 〉 2 : # i ncl ude く std ⅱ b. h > 3 : # i ncl ude く ca l. h 〉 4 : struct tn date; 6 : / * カレンダ作成 : メイン関数 8 : VOid nain(int Char **av) i nt i , j, k. dd : 12 : printf("usage: calen 91 5 ・ 14 : return; atoi(av[l]) : date. tn_year : date. tn_non atoi (av[2]) date. tn_nday -mjd(&date) : 19 : -mday (date. い - 田 on ] : 20 : dd = 22 : : date. tn_wday; J for (i 23 : ・ 1991 年 5 月のカレンダ出力 \ n つ : 年月日 135 ワンポイントプログラミング講座

5. 月刊 C MAGAZINE 1991年6月号

List 1 LlSt 1 455 : } 456 : 457 : 458 : { 459 : 460 : 461 : 462 : 463 : 464 : 465 : 466 : } 467 : 468 : / * 469 : 470 : { 472 : 473 : 488 : / * 507 : / * 539 : / * 564 : / * 589 : / * for( n = 0 : n く TARGETCOUNT,• tod ニ 0 し : if( ( link ー link->next ) int istargetfile( char *name ) return ー 1 : int arg[ 2 ] ニ NU しし : if( arg[ 1 ] ! = NU しし ) fprintf( stdout. "YnY33[7m%s %sY33[0mYn", arg[ 0 ] , arg[ 1 ] ) : e ー se fprintf( stdout, " \ n \ 33 [ 7m % s \ 33 [ 0m \ n " , arg[ 0 ] ) : return spawnv( P_WAIT, arg[ 0 ] , arg ) : n 十十 ) if( ! strcmp( name, TARGET[ n ]. name ) ファイルの日付・時刻を得る uns i gned 1 ong getf ⅱ etod ( char * return unsigned カレント /PATH からファイルを検索する 540 : char * serchpath( char *name ) 542 : char *cp, *commandpath, *pathspec, pathname[ 128 ] : 471 : struct f i nd t buffer : uns i gned i nt date, t i me : unsigned long tOd; name, name ) 0 , &buffer ) ) 474 : 475 : 476 : 477 : 478 : 479 : 480 : 481 : 482 : 483 : 484 : 485 : 486 : } 487 : 490 : { 491 : 492 : 494 : 495 : 496 : 497 : 498 : 499 : 500 : 501 : 502 : 503 : 504 : 505 : } 506 : 508 : 509 : 511 : 512 : 513 : 514 : 515 : 516 : 517 : 518 : 519 : 520 : 521 : 522 : 523 : 524 : 525 : 526 : 527 : 528 : 529 : 530 : if( dos_findfirst( date ニ buffer. wr_date : = buffer. wr_time; tOd ー ( ( unsigned long )date くく 16 time return tod : long )time; コマンドラインを実行してファイルをアップデートする 489 : void update( int number ) struct makelist *link; int status; 493 : char temp[ 32 ] : 531 : 532 : 533 : 534 : 535 : 536 : 537 : } 538 : 541 543 : 544 : 545 : 546 : 547 : 548 : 549 : 550 : 551 : 552 : 553 : 554 : 555 : 556 : 557 : 558 : 559 : 560 : 561 : 562 : } 563 : 566 : 568 : 569 : 570 : 571 : 572 : 573 : 574 : 575 : 576 : 577 : commandpath ニ serchfile( ” name ) : i f ( commandpath ! ニ NU しし ) return commandpath ; pathspec = getenv ( " PATH" ) : for ( : cp ニ pathname : wh ⅱ e ( ( *pathspec ) & & ( *pathspec ! = *CP 十十 = *pathspec 十十 : *cp ニ 0 : commandpath = serchf ⅱ e ( pathname, name ) : i f ( commandpath ! = NU しし ). return commandpath : i f ( ! *pathspec ) break : el se pathspec + + : return NU しし : PATH とファイル名を結合して検索 * / 565 : char * serchfi le( char *path, char *name ) strcpy( pathname, path ) : 567 : char *cp, *sp, pathname[ 128 ] : ⅱ nk = &COMMAND [ number ] : while( link->name ! : NU しし ) error2( "EXECUTE!", temp ) : " EX は STATUS = %d ” , status if( execute( link->name ) ) sprintf( temp, = NU しし ) break; sp ニ cp = path: while( *cp ) if( if( if( *cp ー *sp ) = cp 十 1 : break : } else cp 十十 : コマンドラインを実行する i nt execute ( char * command ⅱ ne 510 : char temp[ 256 ] , *arg[ 3 ] : char *cp, *sp, *commandpath : strcpy( temp, commandline ) : cp ニ temp; 578 : 579 : 580 : 581 : 582 : 583 : 584 : 585 : 586 : 587 : } 588 : 591 ・ 592 : 594 : 595 : 596 : 597 : 598 : 599 : 600 : 601 : 602 : 603 : 604 : 605 : 606 : } n = strl en ( pathname ) : pathname [ n pathname [ n ] pathname [ n + 1 ] strcat ( pathname, name ) : return findcommandfile( pathname ) : while( ( *cp く = if( ! *CP ) error2( "BAD COMMAND LINE. Yn", arg[ 1 ] =cp; = commandpath : arg[ 0 ] if( commandpath = NU しし ) return -1 : commandpath ニ serchpath( sp ) : cp = NU しし : else *cp 十十ニ 0 : if( *cp ) while( ( *cp 〉・ SP = cp; cp 十十 : command ⅱ ne . BAT . COM . EXE ファイルを検索する 590 : char * findcommandfile( char * pathname ) 593 : struct find_t buffer; static char commandpath[ 128 ] : strcpy ( commandpath, pathname ) : strcat( commandpath, if( ! _dos_findfirst( commandpath, strcpy( commandpath, pathname ) : strcat( commandpath. ". com" ) : if( ! dos_findfirst( commandpath, strcpy ( commandpath, pathname ) : strcat( commandpath, if( ! dos_findfirst( commandpath, 0 , &buffer ) ) return NU しし : 0. &buffer ) ) プロジェクト PragmaC return commandpath : return commandpath : 0 第 &buffer ) ) return commandpath : 57

6. 月刊 C MAGAZINE 1991年6月号

ワンポイント プログラミング 講座 す。 基準となる日はべつに私の誕生日て、なく てもなんて、もよいのて、すが , 「この日付は天 文計算に使用されている MJD をもとに計算 しました」というとすごいものを作ってしま ったという錯覚におちいるから不思議て、す。 ( 注 )UT . 世界時 (universal Time) 経度 0 。 ( 基準子午線 ) のイギリスグリ ニッジ天文台における平均太陽時。 , れに対して , 東経 135 。 ( 兵庫県明石市 ) の子午線における平均太陽時を JST . 日本標準時 (Japan Standerd Time) と 準ュリウス日 ( MJD) の計算 日付のプログラムて、のもち方は , Fig. 1 に て、は , 各関数の詳細を見てみま 示す MS ー C の tm 構造体を利用しました。月 しよう の指定が 0 から始まるのと , 年の指定が 1900 門年の判定 年からの年数となっている点に注意が必要 てす。 前述の規定をそのままプログラムに記述 計算には 1 年が 3 月から始まるように 1 月と します。 2 月の年月を補正し公式に代入します。よく 無理に 1 ラインにまとめようとはせずに単 ある公式て、は , スマートに 純に記述したほうが , コンパイラの最適化 MJD = INT [ 365.25 X Y] によってコンパクトなソースコードが生成 + INT [Y / 400] ー INT CY / 100] + INT [ 30.59 x (M ー 2 ) ] されます。 日付計算関数 0 うるう 0 cal.c List 1 List 1 1 : #include く stdio. h> 2 : #define YES 1 3 : #define NO 0 4 : #define OK 1 5 : #define NG 0 6 : struct tm { 7 : i nt tn_sec; 8 : int tm_nin; 9 : int tm_hour; 10 : int tm_mday: int tm_non; int tm_year; 13 : int tm_wday; 14 : int tm_yday; 15 : int tm_isdst; 16 : 17 : / * - 18 : / * 曜日ェリア 20 : char *_wday[7] 22 : / * 月の末日ェリア 23 : / * - 24 : int -mdaYC12] = { 31 , 28. 31. 30. 31. 30. 31. 31, 30. 31 , 30. 31 } : 26 : / * 祝日ェリア ( 振替休日を考慮 ) 27 : / * ー 28 : #define nunber of_horidays 20 29 : -holytbll [number_of_horidays] static int 30 : { 101. 115. 211. 429. 503. 504. 505. 915. 923 , 1010.1103 , 1123. 1223. 0 } : 32 : / * ー 33 : / * 休日ェリア ( その他の休日 ) 34 : / * - 35 : static int -horiday-table-read = NO; 36 : static int -holytb12[number_of_horidays] 37 : 38 : 39 : / * - 40 : / * 関年判定年 . NO : 関年でない ) * / 42 : int leapyear(int yy) 43 : { 44 : if ((yy % 40 の 45 : return(YES) : 46 : if ((yy % 10 の 48 : return(N0) : 49 : 50 : return(YES) : 52 : 53 : return(N0) : 54 : } 56 : / * 準ュリウス日 ( M J D ) の計算 57 : / * ー 58 : long -mjd(struct tm *date) 59 : { 60 : long müd; int y, m, d: 62 : / * 月 2 * / 4 5 6 7 8 9 10 1 1 1 63 : 1 2 3 4 5 6 9 10 1 1 * / 7 8 static yday[] 64 : 92 , 122 靆 53.184 , 214 , 245 認 75 , 306 , 337 } : 0 , 31, 6 1, 65 : if ((*date). tm_mon > : 2 ) 66 : (*date) . tm mday; (*date) . tm 68 : m 田 on (*date) . い 69 : _year; 70 : (*date). tm-yday = 59 + apyea 「 ( 1900 + y) + yday 朝 ] + d,• 71 : else { 73 : 75 : 76 : 77 : 79 : 80 : 82 : } 83 : / * - 84 : / * M J D から年月日を計算 85 : / * ー 86 : VOid -jdate(long mjd. struct tm *date) 88 : int 89 : long 90 : mjdl : mjd + 678882 し : 92 : forC ・ 94 : 95 : 96 : 97 : 98 : 99 : 100 : 101 : 102 : 103 : 104 : 105 : 106 : 107 : 108 : 109 : 110: 112 : 一フ ワ 0 1 よ 0 0 0 0 0 1 ・ 0 ・ー - 日間 ( 日算時 秒分時日月年曜通夏 0 十 0 ・ 1 ↓し 十》・し十》・ 0 cd 0 十・ 0 ・ 0 ー 678882 し : njdl, mjd2: (int) (mjdl / 365 njd2 = y * 365 し + y / 4 - y / 100 + y / 400 + 306 : if (njd2 く mjdl) break: y 十十 : (*date). tn_year - 1900 : Y (*date). tm 田 on (*date). tm_mday (int) (njd - mjd(date)) : J -nday[l) = 28 + leapyear(y) : fo 「 ( i = 0 : i く 12 : i + + ) { -mday[i]; if (j ←の { -mday[i]; break : : の (*date). 田 on ワンポイントプログラミング講座 133

7. 月刊 C MAGAZINE 1991年6月号

際には REGS は Fig. 9 のようなふたつの構造 体からなる共用体て、 , 各メンバは 8086 のレ ジスタに対応しています。共用体になって いるのは , 8086 の 16 ビット汎用レジスタ AX ~ DX が , 16 ビットのレジスタとしても , 8 ビットのレジスタふたっとしても使えるた めて、す。たとえば , AX レジスタの上位バイ トは AH, 下位バイトは AL という 8 ビットレ ジスタとして使うことがて、きます。 intd 。 s 側て、は第 1 引数て、指定された構造体 の内容をレジスタにコヒ。ーして INT 21H を 発行し , その時点て、のレジスタの値を第 2 引 数て、指定された構造体に収めて戻ります。 関数の戻り値は AX レジスタの値て、す。ま た , 結果を格納する REGS 構造体の cflag メ ンバが非 0 のときはエラーを意味し , AX に は DOS のエラーコードが入っています。た だし , MS-DOS て、はすべてのファンクショ ンがエラーを返すわけて、はなく , CP / M 互換 の ( コール番号の若い ) ファンクションて、は cflag は無意味て、す。 て、は実際の移植を考えましよう。まずコ ール番号を確認します。前述のようにコー ル番号は AH レジスタて、指定するのて、 , 引数 受け渡しに使う構造体の ah メンバに値を代 入している部分を探します 0Fig. 8 ( c ) て、は直 接 ah は現れませんが , AH は AX レジスタの Fig. 9 intdos( ) で使われる構造体 struct WORDREGS { unsigned int ax; unsigned int bx; unsigned int cx; unsigned int unsigned int Si ; unsigned int di ; unsigned int cflag' struct BYTEREGS { unsigned char al, ah; unsigned char bl' bh ; unsigned char cl, ch; unsigned char 引 , dh' union REGS { struct WORDREGS x; struct BYTEREGS h; 86 C MAGAZINE 1991 6 上位バイトて、す。メンバ ax に代入している 値の上位バイトがコール番号となります。 Fig. 8 ( c ) て、は AH レジスタに設定すべき値と AL レジスタへ設定すべき値を同時に代入し ていたわけて、す。 さて , intdos て、はセグメントアドレスを DOS ファンクションに渡すことがて、きません。 far ポインタを DOS ファンクションに渡す必 要があるときには intdosx が使われます。 Fig. 8 ( e ) に , DOS ファンクション 41H を使ってフ ァイルを削除する例を示しておきます。 こて、使用している ,FP SEG,FP OFF は far ポインタのセグメント部 , オフセット部を 取り出すマクロて、す。このふたつのマクロ を使って far* インタを分解し , それぞれレ ジスタ ( 対応した構造体メンバ ) に代入して いるわけて、す。 Human68k への移植時には こんな処理は不用て、すから , 素直に対応す る DOS コール $FF41(DELETE) て、置き換え れば , Fig. 8 ( f ) となります。 ところて、 , bdos や intdos を使わなければ DOS ファンクションが呼び出せなかったの は少し前まて、のことて、 , Turbo C 以来 , MS ー DOS 上の C 処理系て、もよく使われるファン クションについては独立した関数を提供す る傾向にあります。ここて、 , その中て、も使 用頻度の高い , MS-C の dos findfirst, dos findnext, Turbo C の findfirst, findnext を取り上げましよう。関数名は異なります が , どちらもファンクション 4EH, 4FH に よりファイルを検索し , その情報を得るも のて、す。移植時には , XC の相当関数 , FILES, NFILES に置き換えることになり ます。厳密には , 工ラー時の戻り値が , MS ー C 版て、は正の DOS ェラーコード , Turbo C 版て、は一 1 , XC 版て、は負の DOS 工ラーコー ドというように異なっていますから ( 正常終 了時はいずれも 0 を返す ) , 単純な置き換え て、は不十分なのて、すが , この違いが問題に なることはあまりないて、しよう。 実際に関数を置き換えるときには , 引数 の違いに注意します。 dos findfirst, findfirst, FILES はいずれも , 検索するファ イル名 , 検索するファイル属性 , ファイル 情報を受け取る構造体へのポインタの三つ の引数をとりますが , 引数が並ぶ順序と , ファイル情報を受け取る構造体の名前 / メン バ名が異なっています。例によって , マク ロて、つじつまを合わせることになるて、しょ う。 Fig. 10 ( a ) に dos findfirst を FILES に置 き換えるマクロ , Fig. 10 ( b ) に findfirst を FILES に置き換えるマクロをそれぞれ示し ます 0Fig. 10 て、は同時にファイル属性を表 す記号定数も , 各処理系に揃えて定義して います。 0X20 との OR を取っている部分が不 可解かもしれませんが , これは MS-DOS フ アンクション 3EH と Human68k の DOS コー ル $ FF3E ( FILES ) の仕様の微妙な違いを吸 収するためのものて、す。 MS-DOS ファンク ション 3EH て、は , 指定した検索属性に関わ らず通常ファイルは無条件に検索対象にな るのに対し , Human68k の FILES て、は , ア ーカイプビットを立てておかないと通常フ ァイルの検索が行われません。 MS-DOS と Human68k て、はほかにもファ イル属性の扱いが異なる場面が見られます。 ファイル属性を取得 / 設定する MS-DOS ファ ンクション 43H て、は , ディレクトリとポリュ ーム名の属性ビットを変更することを許し ていませんが , Human68k の DOS コール $FF43(CHMOD) て、はこれらビットも操作て、 きます。 DOS ファンクション 43H て、は , ディレク トリやポリューム名の不可視属性や読み込 み専用属性を設定するときに , ディレクト リやポリューム名を意味するビットを立て てはならないという仕様になっており , そ れを知らずに DOS ファンクション 43H を単 純に Human68k の CHMOD に置換すると , ディレクトリがファイルに化けてしまうと いうことも起こるのて、す。この場合 , 現在 のファイル属性を取得して , ディレクトリ やポリューム名のビットを保存するような 細工が必要て、す。

8. 月刊 C MAGAZINE 1991年6月号

LISt 1 379 : / * 303 : 304 : 305 : } 306 : 308 第 309 : { 310 : 311 : 312 : 313 : 314 : - 315 : 316 : 317 : 318 : 319 : 320 : 321 : 322 : 323 : 324 : 325 : 326 : 327 : } 328 : 330 : 331 333 : 334 : 335 : 336 : 337 : 338 : 339 : 340 : 341 : 342 : 343 : 344 : 345 : 346 : 347 : 348 : 349 : 350 : 351 : 352 : 353 : 354 : 355 : 356 : 357 : 358 : 359 : 360 : } 361 : 362 : 363 : 364 : 365 : 366 : 367 : 368 : 369 : 370 : 371 : 372 : 373 : 374 : 375 : 376 : 377 : } 378 : COMMANDLINK->next = N 乢し : TARGETCOUNT + + : ・ VO i d set_command ( char * ⅱ ne 307 : / * コマンドラインの登録 char *cp : List 1 MAKE 処理 381 : #define MAXNEST 8 int NESTLEVE し while( *line くニ if( ! *line + + ) if( VERBOSE ) printf( " n 十十 ) return : by [%s]Yn". line ) : NEST し EVE し for( n ニ 0 : n く TARGETCOUNT; maketargetf ⅱ e ( n ) : if(( cp ニ malloc( strlen( line ) + 1 ) ) strcpy( cp. line ) : COMMANDLINK->name = cp; error( make ⅱ st ニ NU しし ) "MEMORY / COMMAND" ) : int maketargetfile( int number ) 397 : struct _makelist *link; 398 : char *makename, vertab[ MAXNEST + 1 ] : COMMANDLINK->next ニ malloc( sizeof( struct COMMANDLINK = COMMAND い NK->next; COMMANDLINK->name : NU しし : COMMAND い NK- 〉 next ニ NU しし : 329 : / * マクロを拡張する * / int expand_macro( char *cp, char *sp ) 332 : char cc, *mp, macroname[ 256 ] : switch( *sp ) *cp 十十 = i f ( ( cc while( *SP ) sp 十十 : CC . = *SP 十十 ) ! ニ while( ( cc = *SP + + ) ! = mp = macroname : if( ! cc ) error( "BAD MACRO" ) : = NU しし ) error( "BAD MACRO" ) : if( cp cp = convert_macro( cp, *mp : 0 : *mp 十十ニ macroname ) : break : return 0 : *cp = 0 : 380 : 382 : 383 : 385 : { 386 : 387 : 388 : 389 : 390 : 391 : 392 : 393 : } 394 : 395 : 396 : { 399 : 400 : 401 ・ 402 : 403 : 404 : 405 : 406 : 407 : 408 : 409 : 410 : 411 : 412 : 413 : 414 : 415 : 416 : 417 : 418 : 419 : 420 : 421 : 422 : 423 : 424 : 425 : 426 : 427 : 428 : 429 : 430 : 431 : 432 : 433 : 434. 435 : 436 : 437 : 438 : 439 : 440 : 441 : 442 : 443 : 444 : 445 : 446 : 447 : 448 : 449 : 450 : 451 : 452 : 453 : 454 : int n; 384 : void makefiles( void ) int n; uns i gned long targettod : ・ uns i gned long dependtod : if( VERBOSE ) if( NEST し EVE し ) memset( vertab, vertab[ NESTLEVE し ] link = &TARGET[ number ] : makename link->name; if( VERBOSE ) ・ Yt ・ , NESTLEVE し ) : printf( "%sTARGET#%02d Y"%sY"Yn". vertab, while( link->next ! : NU しし ) ⅱ nk link->next; if( VERBOSE ) printf( "%sdependent Y"%sY"" vertab, number 十 1 , makename ) : link->name ) : if( ( n istargetfile( link->name ) ) く 0 ) if( VERBOSE ) printf( ” Yn ” ) : e ー se if( VERBOSE ) printf( " is TARGET#%02dYn ” , n + 1 ) : if( NEST し EVE し〉 : MAXNEST ) error( " T00 MANY NESTING" ) : char * convert_macro ( Char *cp, int n; char *sp : for( n = 0 : n く MACROCOUNT; Char n 十十 ) *name NEST し EVE い + : maketargetfile( n ) : NEST し EVE し一 link : &TARGET[ number ] : makename link->name; if( VERBOSE ) pr i ntf ( "%sMAKI NG#%02d \ " %sY " Yn". vertab, number + l, targettod = getf ⅱ ( makename ) : while( link->next ! : NU しし ) link ー link->next: dependtod = getfiletod( link->name ) : if( ! dependtod ) error2( "NOT FOUND - > " i f ( targettod く dependtod ) update ( number ) : targettod ニ getf ⅱ etOd ( makename ) : makename ) : if( ! strc 叩 ( MACRO[ n ]. name. name ) ) sp ニ MACRO[ n ]. line: while( *CP = *sp 十十 ) cp 十十 : return cp : return NU しし : link->name ) : i f ( ( targettod く dependtod ) Ⅱ ( ! targettod ) ) error2( "NOT UPDATE - 〉 " , makename ) : 56 C MAGAZIN E 1 1 6

9. 月刊 C MAGAZINE 1991年6月号

List 1 227 : / * マクロ定義 * / VOid set macro( Char *name, 151 : 152 : 153 : 154 : 155 : 156 : 157 : 158 : 159 : 160 : 161 : 162 : 163 : 164 : 165 : 166 : 167 : 168 : 169 : 170 : 171 : 172 : 173 : 174 : 175 : 176 : 177 : 178 : 179 : 180 : 181 : 182 : 183 : 184 : 185 : 186 : 187 : 188 : 189 : 190 : 191 : } 192 : 193 : 194 : { 196 : 197 : 198 : 199 : 200 : 201 : 202 : 203 : 204 : 205 : 206 : 207 : 208 : 209 : 210 : 211 : 212 : 213 : 214 : 215 : 216 : 217 : 218 : 219 : 220 : 221 : 222 : 223 : 224 : 225 : } 226 : line[ 256 ] : 257 : / * if( ( cp = malloc( n + 1 ) ) = NUL し ) error( "MEMORY ERROR" ) : fp ) ) ニ NULL ) error( "MEMORY ERROR" ) : " T00 MANY TARGET ” ) : List 1 Char CC, *cp, *pre, *next int targetnumber, n,• char *linkpointer; , buffer[ 256 ] , 228 : 229 : char *line ) targetnumber ニ 0 : linkpointer ニ NU しし : fp ニ fopen ( makef ⅱ ename, if( ( cp ニ fgets( buffer, while( ! feof( fp ) ) abort() : fprintf( stdout, i f ( fp - = NUL し ) ” MAKEFlLE=%sYn ” 256 , makefi ニ NULL ) break : while( cp ) if( *cp cp 十十 : if( buffer[ 0 ] = if( line[ 0 ] : 0x0a ) { *cp : 0 : break,• ) continue; while( expand_macro( line, buffer ) ) set_ target _or_macro ( ⅱ ne ) : set-command( line e ー se fclose( fp ) : void set_target_or 195 : char name[ 256 ] : char *CP, *dp; while( *cp くニ *dp : 0 ; *dp 十十 : *cp 十十 : if( *cp = if( *cp while( *cp 〉・ dp ニ name; CP line; _mac ro ( Char ⅱ ne ・ ) break; ・ ) break; switch( *cp+ 十 ) cp 十十: if( ! *CP ) break; error( 230 : 231 : 232 : 233 : 234 : 235 : 236 : 237 : 238 : 239 : 240 : 241 : 242 : 243 : 244 : 245 : 246 : 247 : 248 : 249 : 250 : 251 : 252 : 253 : 254 : 255 : } 256 : 258 : 259 : 262 : 263 : 264 : 265 : 266 : 267 : 268 : 269 : 270 : 271 : 272 : 273 : 274 : 275 : 276 : 277 : 278 : 279 : 280 : 281 : 282 : 283 : 284 : 285 : 286 : 287 : 288 : 289 : 290 : 291 : 292 : 293 : 294 : 295 : 296 : 297 : 298 : 299 : 300 : 301 : 302 : int n; char *cp; wh ile ( * ⅱ ne ← if( ! *line + + ) error( "SYNTAX... Missing MACROSTRING" ) : strcpy ( cp, name ) : if( ( cp : malloc( n + 1 ) ) ニ n ニ strlen( name ) ; MACRO[ MACROCOUNT ]. line strcpy( cp, line ) : n = strlen( line ) : if( VERBOSE ) MACRO[ MACROCOUNT ]. name ニ cp; printf( "Define $(%s) to Y"%sY"Yn", MACROCOUNT + + : MACRO[ MACROCOUNT ]. name, MACRO[ MACROCOUNT ] コ ine ) : link->next ニ NULL,• strcpy( link->name, name ) : link->name ニ malloc( strlen( name ) + 1 ) : ⅱ nk = &TARGET [ TARG ETCOUNT ] : 261 : struct _makelist *link; 260 : char *cp, *sp, temp[ 128 ] : VOid set_target( char *name, char *line ) ターゲットリストの登録 * / while( *CP くニ while( *cp ) CP ⅱ ne : link->name ニ malloc( strlen( temp ) + 1 ) : link->next; ⅱ nk link->next = malloc ( sizeof( struct makelist ) ) : *SP ニ 0 ; *sp 十十ニ *cp 十十 : while( *CP > ' sp ニ temp : if( ! *cp ) break,• if( ! *cp ) break; else cp + 十 : strcpy ( link->name, temp ) : link->next : NU しし : if( VERBOSE ) link ニ &TARGET[ TARGETCOUNT ] : printf( "Target %s: Dependent". while( link->next い NU しし ) link->name ) : case case if( TARGETCOUNT > ニ MAXTARGET ) set-target( name, cp ) : break : if( MACROCOUNT 〉ニ MAXMACRO ) link link->next; printf( " Y"%sY"" printf( ” Yn ” ) : link->name ) : set_macro ( name, cp ) : break : default: error ( ” SYNTAX ERROR.. error( MANY MACRO" ) : or Expect i ng COMMANDLINK ニ &COMMAND[ TARGETCOUNT ] : COMMANDLINK- 〉 name ニ NU しし : プロジェクト PragmaC 55

10. 月刊 C MAGAZINE 1991年6月号

、、 far", 、、 huge" というポインタ型修飾子を使 って , たとえば , cha 「 far *p ; のように宣言します。修飾子をつけずに cha 「 * p ; と , ふつうに宣言した場合は , コンパイル 時に指定したメモリモデルによって決まる デフォルトのポインタサイズが適用されま す。メモリモデルというのは , デフォルト のポインタの種類を決めるものだと思って ください さて , 68000 にはセグメントなどという不 自然なものはありません。ポインタに near と far の区別もなければ , メモリモデルも存 在しません。長々と説明してきましたが , 移植時にはセグメントにまつわる部分をす べて無視すればよいのて、す。プログラム中 て、 near, far といった修飾子が使われていた ら , 無条件に削除します。これらは MS ー DOS 上の C 処理系て、は予約語て、あり , 同名の変数 が定義されている心配はありませんから , 工デイタなどて、すべて空文字列に置き換え てしまっても大丈夫て、す。さもなくば , プ ログラムの冒頭に Fig. 4 のような空のマクロ 定義を追加し , プリプロセス時に削除する 方法もあります。ヘッダファイルにまとめ ておき , 移植時に必ず取り込んて、もよいて、 また , セグメントとは関係ありませんが , MS ー DOS 上の C 処理系ては関数の呼び出し 規約を指定する $cdecl", $pascal" といった キーワードも追加されています。移植時に は , これらのキーワードも同様の方法て、削 除 / 無効化しましよう。 アイ、メントの間題 これは 68000 側の制限て、す。 68000 てはコ ード , データ ( バイトサイズを除く ) は基本 的に偶数アドレスに置かなければなりませ ん。奇数アドレスから 16 ビット , 32 ビット 単位てメモリアクセスしようとすると , お Fig. 5 Fig. 6 隙間のある構造体 (a) struct f00 char CI; char C2; char C3; / * 1 バイトの詰め物 * / アライメントの問題 (a) chara [ 10 ] ・ int * p = &a [ 1 ] ・ intval; ne Oint Edition 0 P struct f00 { char C; / * 1 バイトの詰め物 * / charaCIO] ・ a [ 2 ] intval & Ox 幵 ; (intval > > 8 ) & Ox 幵 ; 馴染みのアドレスエラーが発生します。も ちろん , 68000 の C コンパイラはこのあたり の事情を把握していますから ,short や int の 変数が必す偶数アドレスに置かれるよう , 必要に応じてレヾイトの詰め物を入れてワー ド境界に整合します。たとえば , Fig. 5 のよ うな char 型メンバを奇数個もった構造体て、 は , 図に示したような位置に 1 バイトの詰め 物が入ります。 ( a ) はともかく ( b ) の場合も , 構造体を配列にすることを考慮し , 末尾て、 偶数アドレスに整合することに注意してく ださい 86 系 CPU はアラインメントに関しては 68000 よりも制限が緩やかて、 , 偶数境界をま たいて、メモリアクセスしてもエラーになり ません。ただし , CPU はメモリアクセスを 2 回に分けて行うことになるため , 実行速度 は低下します。この効率低下を嫌って 86 系 の C てもデータを偶数アドレスに整合する場 合もあるようて、す。 さて , この両プロセッサの違いが移植に どのように影響するかて、すが , 移植元のプ ログラムが常識的なコーディングになって いれば , ほとんど影響しないといってよい て、しよう。すて、に述べたように いちばん 問題になりそうな構造体に関しては C コンパ イラがきちんとつじつまを合わせてくれま す。ただ , 構造体のメンバがすべて隙間な く並んて、いることを前提にするプログラム て、は問題が発生します。また , 構造体を使 えばすむところを char 配列とインデックス て、操作するプログラムになると , かなりめ んどうなことになります。 Fig. 6 ( a ) はその 後者のケースを単純化した例て、す。 char 配 列の奇数バイト位置に int データを書き込ん て、います。この場合は int のサイズとエンデ ィアンの違いまて、絡むのて、 , Fig. 6 ( b ) のよ うに修正します。 Human68k は , 外見だけて、はなく内部仕 様まて、 MS ー DOS を参考にして作られていま す。しかし , よく似ているからこそ移植時 には細部の相違に注意しなければなりませ ん。 OS One Point Edition 83