void - みる会図書館


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

1. 月刊 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

2. 月刊 C MAGAZINE 1991年6月号

関数へのポインタ N mo 化 第 7 回 きだあきら してきた。今回は関数へのポインタについて解説する。 前回までは ( テータ ) オプジェクトへのポインタについてお話 関数へのポインタ くわからないとかいう場合が少なくないよ か関数へのポインタは苦手てあるとか , よ 通りポインタをマスターした人ても , なぜ タは難解て、あるという話をよく聞く。ひと い。それにもかかわらず , 関数へのポイン 扱うことがて、きるのは不思議なことてはな らないのだから , 関数へのポインタを取り する型の情報を備えたアドレス値にほかな してポインタは , 端的にいえば対象物に関 して操作することは可能なはずて、ある。そ 数と同様に , 関数もそのアドレスを取り出 て、メモリ上に記憶されるのて、あるから , 変 を除けば , 変数も関数もすべて何らかの形 考えてみれば , 記憶クラスが register の変数 になる。 C 言語のインプリメンテーションを 「関数」という実体を間接的に指し示すこと ている。関数へのポインタは , 当然ながら さて , C には関数へのポインタが用意され て、ある。 制限 ( 自由度と考えるべきか ? ) がっくだけ オプジェクトの型は何て、あるか不明」という 変わりはない。ただ void * は「ポイント先の ェクトを指し示すためのものて、あることに が違ってくるが , それて、も何らかのオプジ のものて、ある。なお , void * の場合少し話 体 ( オプジェクト ) を間接的に指し示すため C におけるポインタは , 一般に具体的な実 関数へのポインタは , 関数を間接的に呼 び出すための機能て、あるとも解釈て、きる。 目的とする関数名を指定して呼び出すのが , 一般的な直接呼び出して、ある。それに対し て , あらかじめ目的とする関数へのポイン タを , 「関数へのポインタ」型の変数に格納 し , そのポインタ変数の値を利用して呼び 出すのが「間接呼び出し」て、ある。「間接呼び 出し」の機能をもつ言語は C だけて、はない 有名なところて、は ,PascaI や FORTRAN に も存在する。さらに Lisp 系の言語て、は間接 呼び出しは日常茶飯に用いられている。し かし , それらの言語て、は , 間接呼び出しを 行うことが困難な作業て、あるとは認識され ていないようだ。て、はなぜ , C における関数 へのポインタは , 苦手意識をもって受け止 められるのだろうか。 関数へのポインタを難解なものにしてし まっている理由として , ーっはその構文的 な記法の問題があると思われる。関数への ポインタ funcp を宣言する場合は , 以下のよ うに書かなくてはならない ( int を返す関数へ のポインタ funcp , 引数の型情報は省略して いる ) 。 int (*funcp)( ) ; また funcp を通じて関数を呼び出す際に ANSI 以前のコンパイラて、は次のように宣言 と類似した形式て、書く必要があった ( 引数は ないものとする。また , 呼び出した関数が 返してきた値は無視している ) 。 これらの記法は , 見慣れた普通のポイン タ変数宣言や , 関数呼び出しとかなり異な っており , 一見して難解て、あると感じさせ てくれる。大げさにいえば , これらの記法 は , それを見慣れていない人にとっては何 のことか , わけのわからない呪文に等しい このことが , 関数へのポインタに対する最 初の , そして最大の問題て、あろう。しかし ながら , 幸いなことに ANSI 標準とそれ以前 のいくつかの処理系て、は , 以下のように記 述することが可能て、ある。 funcp( ) ; これならば , 通常の関数の直接呼び出し とまったく変わらない。この記述を可能に するために , ANSI て、は関数呼び出しに関す るセマンティクス ( 意味的解釈 ) を K & R のも のとは変更している。これは非常に興味深 ことなのて、後て、詳しく述べる。 ただ , 関数へのポインタを通して関数を 呼び出す場合には上述のように直接呼び出 し的な記述が可能になったが , 残念ながら funcp を変数として ( 関数の仮引数以外の場 所て、 ) 宣言する場合には , ANSI ても最初に あげたとおりの int ( * funcp)( ) ; という記述 を強いられる。 関数へのポインタが難解て、あるもうーっ の理由は , その必要性・必然性が理解され こともあるのてはないかと考えてい る。多くの教科書が適切な使用例を示して ことが , 関数へのポインタに対する 理解を阻んているのてはないだろうか。 ANSI C ー more 105

3. 月刊 C MAGAZINE 1991年6月号

, 新 MS ー DOS プログラミンク入門。 : 第 C プロクラマのための TabIe 4 ドライプの取得 dos getdrive 関数 (MS-C ver. 5.1 / 6.0 , LSI C ー 86 Ver. 3.20 ) #include <dos. h> void dOS getd 「 ive (drive ) : unsigned * drive, 返り値なし getdisk 関数 (Turbo C Ver. 2.0 , Turbo C 十十 Ver. 1 .0 ) #include く dir. h> int getdisk ( VOid ) ; 返り値カレントドライプ番号 ( 0 = A, 1 ドライプ移動を伴うティレクトリ移動コマンド ( chdi Ⅸ . c ) TabIe 3 カレントティスクの取得 DOS ファンクション 19h カレントティスクの取得 引数 AH = 19h INT 21 h カレントドライプ番号 ( 1 = A, 2 返り値 AL = 現在選択されているドラ イプ ( 0 = A, 1 B, AL レジスタに現在選択されてい るドライプを返す 機能 というふたつのコマンドをパイプて、接続し たコマンドて、す。 d:lCd *subdir を C 言語で書くと ドライプを変更する DOS ファンクション は DOS ファンクション 0Eh(Table 1 ) て、す。 DOS ファンクション 0Eh を呼び出すライプラ リ関数を TabIe 2 に示します。 ただし , DOS ファンクション 0Eh て、はドラ イプを変史て、きたかどうかは確認てきない ため , ドライプの変更を確認するためには DOS ファンクション 19h(TabIe 3 ) て、移動後 のカレントディスクを取得して移動先と比 較する必要があります。 DOS ファンクショ ン 19h を呼び出すライプラリ関数を TabIe 4 に示します。 これだけの準備が整った後はおなじみの chdir 関数と組み合わせれば , ドライプ移動 を伴うディレクトリ移動コマンドのプログ ラムが完成します。プログラムを List l(chdir x. c) に示します。 ドライプ移動を伴う ティレクトリ移動 それて、は UNIX ライク ( もどき ? ) のユーテ ィリティて、採用されている , よりまともな ドライプ移動を伴うディレクトリ移動コマ ンドを作成しましよう。作成したプログラ ムが List 2 ( chd. c : 本ユーティリティは日常 新 MS-DOS プログラミング入門 75 List ト・ライフ・移動を伴うテ・イレクトリ移動 ] マント・ ファイル名 : chdirx. c 6 : 使用法 : chdirx くテ・イレクトリ > 7 : 8 : 9 : コンハ・イルスイッチ 11 : MS-C 5. 1 / 6.0 : 12 : 13 : /J /W3 /Zp chdirx. c /link / st : 0X2800 / cp : 0X1000 14 : 15 : Turbo C 2.0 / Turbo C + + 1.0 ・ 17 : tcc -w chdirx. c 18 : 19 : LSI C ー 86 Ver 3.20 20 : 21 : lcc chd i rx. c 22 : 23 : * / 24 : 25 : #include く stdio. h> 26 : #include く ctype. h> 27 : #include く dos. h> 28 : #ifdef TURBOC_ 29 : #include く dir. h> 30 : #else 31 : #include く direct. h> 32 : #endif 33 : 34 : #define ERR 35 : #define NOERR 0 36 : 37 : i nt mvd i r ( char * ) : 38 : 39 : int main( int argc, char *argv[] ) 40 : { if ( argc くニ fputs( ” usage : chdirx d:YYdirYn ” 42 : return( 1 ) : 43 : 44 : i f ( mvd i r ( argv [ 1 ] ) ) { 45 : fputs( " テ・イレクトリの移動に失敗しました。 Yn", 46 : return( 1 ) : 48 : return( 0 ) : 49 : 50 : } 52 : int mvdir( register char dir[] ) if ( ! ( dir[ 1 ] : 54 : & & dir[ 2 ] : 55 : & & chd i r ( d i r ) ) 56 : return( ERR ) : / * 工ラー時のリターンコート・ stderr ) : stderr ) : / * テ・イレクトリの移動 ・ Y0 ・ ) / * ト・ライフ・指定 * / / * テ・イレクトリの変更

4. 月刊 C MAGAZINE 1991年6月号

新、 ' - 撝 プロクラノグ入門 C プログラマ のための 行 島 中 第 9 回ラレクトリの移動 ティレクトリを変更する場合 , 内部コマンドの C HD 旧 ( CD ) コマンドを使用します。しかし , この コマンドはティレクトリを変更するのみてドライ プの移動を伴いません。今回は , ドライフの移動 を伴うティレクトリ移動コマンドの作成に取り組 みます。 ライプ移動を伴うディレクトリ移動コマン ます。 DOS コマンドによる ドを作成すると , ティレクトリ移動 cd *subdir d : ' 90 年 12 月号「リダイレクト機能 ( その 3 ) 」て、 といった形て、実現されます。これて、ドライ も少し解説しましたが , DOS コマンドて、ド プ d : に移動してディレクトリ \ subdir に移り TabIe 2 ドライプの変更 Table 1 ティスクの選択 dos setdrive 関数 (MS-C Ver. 5.1 / 6.0 , LSI C ー 86 Ver. 3.20 ) DOS ファンクション OEh ティスクの選択 #include く dOS. h> 引数 AH = OEh DL = ドライプ番号 VOid dOS setdrive ( drivenum, drives ) , ( 00h = A, 01h = B, ・ " ) unsigned drive, 変更するドライプ番号 ( 1 ー A, 2 ー NT 21 h unsigned * drives, 使用可能なドライプ数 返り値なし setdisk 関数 (Turbo C Ver. 2.0 , Turbo C 十十 Ver. 1 .0 ) #include く dir. h> int setdisk ( drive ) : int drive, 返り値使用可能なドライプ数 これは , d : cd *subdir B, AL = 論理ドライプの台数 返り値 カレントドライプを DL レジスタ 機能 で指定されたドライプに変更し て論理ドライプの台数を返す。 論理ドライプとは通常のティス クドライプに加えて RAM ティス クネットワークドライプなどの ドライプのことをいう 変更するドライプ番号 ( 0 ー A, 1 B, 74 C MAGAZINE 1991 6

5. 月刊 C MAGAZINE 1991年6月号

かび上がってきます。第 1 点は , 途中て、数値 たのて、 , データ構造について少し詳しく説 ろがあります。それは List 3 の 8 行目て、す。 明します。 がマイナスの値になってしまい , 大きな数 int ninzu まず次の問題を考えましよう。 を正しく出力て、きなかったことて、す。第 2 点 となっています。このように変数を宣言す るときは同時に初期値を指定て、きます。 は , それぞれの数値がつながって表示され 32 , 000 から 100 , 000 まて、の数値を出力 てしまうことて、す。たとえば 8 桁ごとに表示 本的なデータ構造 するようなことがて、きたら表示が整うはず ( 表示 ) せよ て、す。これらふたつの間題点を解決してい N. Wirth 教授の著書のタイトルに「アルゴ きましよう。 これまて、に解説したことのみを利用して リズム十データ構造 = プログラム」がありま 前回まて、は , 整数を表す int 型と浮動小数 プログラムを作成すると , List 4 のようにな す。たとえば足し算ひとつを取り上げても , 点を表す float 型のみを使いました。しかし ります。ところがこのプログラムを実行す そこには「足す」という演算と足すべき数 ( = ると , Fig. 5 のような結果になってしまいま List 4 の実行結果からもわかるように , それ データ ) とが存在します。これまて、はどちら す。 らを取り扱うことのて、きる数値の範囲には かというと前者に重きをおいて説明しまし この実行結果から , ふたつの問題点が浮 かぎりがあります。 C 言語にはいろいろな型 大きな数値を表示するプログラム Fig. 3 飛び越し文の構文図 LiSt 1 : # i nc 1 ude 2 : 3 : int main(void) 5 : 6 : 7 : 8 : 9 : 10 : } く stdio. h> 飛び越し文 int 90t0 文 continue 文 break 文 retu 「 n 文 for (i = 32000 : i く 100000 ; i + + ) printf("%d" return(0) : Fig. 5 List 4 のプログラムの実行結果 32000320013200232003320043200532006320073200832009321003210132102321033210 ( 路 ) -32768 ー 32767 ー 32766-32765-32764 ー 32763-32762 ー 32761 ー 32760 ー 32759-32758 ー 32757 ー 32756 ー 3 ( 略 ) Fig. 4 文の構文図 文 ラベル付き文 式文 複文 選択文 繰り返し文 飛び越し文 Fig. 6 算術型 列挙型 インテグラル型 算術型 整数型 基本型 浮動小数点型 116 C MAGAZINE 1991 6

6. 月刊 C MAGAZINE 1991年6月号

Fig. 2 ティスプレイレイアウトシートを用いた画面設計 TITLE NO DATE l€e- 12 13 14 15 16 17 18 19 m 21 2 24 為 27 内 31 引 34 37 関 39 40 0 $ コマン物 コマン 6 解・・夥 ~. 20 ス / の . 6 16 2 3 7 ロ、こィ 4 5 6 7 112 8 1 9 1 1 印 176 12 ( 爻んた・心ハヾース ) ) 1 13 14 15 16 イ〒 ( 交ヤ分だ咜旧、・一ス ( 0 ) ) 18 21 ド OR 駅 AT / : をライフのフロラビ〒、イス ' 7 代し手す 印キー 1 2 3 4 5 6 ー 8 9 10 11 12 日 14 15 16 リ 18 19 幻お 24 る 2 ′四 31 爻ア凶 41 0 材お 47 お引盟引引田引ロ田新新 6 ′田田 71 々 73 乃「 6 宿、上 : x 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 21 23 24 27 四 31 32 33 関 37 関 39 40 お 亥い 懸ゞー 00S : ② MS-DOS のバージョンの取得および表 ③ 上下矢印キーによるポックスカーソル ( リバース表示 ) の移動 実行ファイルの起動やスクロールといっ た機能は組み込まれていない。これらの機 能を組み込むためのプログラミングについ ては次回て、紹介する予定て、ある。 Fig. 1 右に「 C の道具箱」を応用して作成し た画面を示す。 時間のリアルタイム表示のためには割り 込み処理を使用せざるをえない。しかしこ の割り込み処理のプログラミングは , なか なか難しい。筆者は , 当初時間のリアルタ イム表示などそれほど難しいものて、はない 90 C MAGAZINE 1991 6 test. c (cIock2. tst) List 1 1 : #def ine EXTERN 2 : #include "config. h" 3 : 4 : # i nc I ud e く stdio. h> 5 : #include く string. h> 6 : # i ncl ude く conio. h> 7 : # i nc 1 ud e く process. h> 8 : # i nc 1 ude く string. h> 9 : 10 : #include ” pldwn. h" 11 : #include "textkei2. h ” 12 : #include menurd. h ” 13 : 14 : # i nc 1 ude "cboxprot. h ” 16 : void main() 18 : i nt i : 19 : char dosver[20]; 20 : char xs [ 20 ] : 21 : struct Gmsmenu mn[20] : 22 : 23 : CLS; 24 : CUROFP;

7. 月刊 C MAGAZINE 1991年6月号

N mul, more List 11 : { 13 : } 16 : { 18 : } 25 : { 38 : } 44 : { 56 : } く stdio. h 〉 1 : #include 2 : 4 : 5 : 7 : 9 : 10 : 12 : 14 : 15 : 17 : 19 : 23 : 24 : 26 : 28 : 29 : 30 : 32 : 33 : 34 : 35 : 36 : 37 : 39 : 41 : 42 : 45 : 49 : 50 : 51 : 52 : 53 : 54 : 55 : 呼び出すべき関数へのポインタを返す関数 int add(int re turn int sub(int n, re turn int mul(int n, return n 十 m,• n, i nt i nt i nt 20 : #define F 3 22 : / * 引数の値に応じて , 呼び出すべき関数へのポインタを返す関数 int (*getFunc(int n)) (int, int) static int (*tableCF]) (int, int) exit(l); fprintf(stderr, if (n く 0 Ⅱ n > = F) { / * 工ラーチェック * / mul, sub, add, 'getFunc. IS invalid argument*n" re turn 10 tableCn] ; 40 : #define N int 43 : main(void) for (k = 0 ; k く F; + + k ) { for (i = 1 ; i く N ; + + i ) { return 0 ; printf("*n") ; printf( ” frl' ) ; printf ぐ、 hd", getFunc(k) (i for (j この宣言は典型的な関数へのポインタ配 列の宣言のパターンを示しているのて、はあ るが , 慣れない人にとってはおそらく非常 に難解に見えるだろう。まず static て、ある が , これは単に効率向上のためにつけただ けなのて、外してもかまわない。また” 下は初期化子 (initializer) て、 , 配列変数の宣 言と同時に初期値を与えるためのものて、あ る。したがって , これも宣言の理解のため に外すことにする。もちろん , 初期化子を 外した場合には別途 table の各要素に値を代 入しておかないとプログラムの動作が変わ ってしまうにの場合不当なアドレスへの関 数コールが起きて , プログラムは暴走する ードウェアにメモリ保護機能が備え カ ) ノ、 られている場合には , メモリ保護違反て、 OS にトラップされることになるだろう ) 。これ らの付加物を取り除くと , 宣言は次のよう int (*tabIeCF])(int, int); これて、もまだなんだかよくわからないの て、 , とりあえず (*tabIeCF]) 全体を TabIe と 書くことにする。すると次のようになる。 int Table(int, int); これならば , ようやく見慣れた形になっ てきた ( はずて、ある ) 。この宣言をこのまま 信じれば , Table という名の関数宣言 ( プロ トタイプ宣言 ) て、ある。どんな関数かという と , int 型の引数を二つ受け取り , int を返す ような関数てあるということになる。 今度は ( * table [ F ] ) の解釈をしてみよう。 まずこれは ( * (table [F] ) ) というように CF] の結合のほうが強い。したがって , table [ F ] が最初に解釈すべき対象て、ある。 tabIeCF] F 要素の配列 table そうして , 次はその一つ一つの要素に * がっくのて , これは「ポインタ」て、ある。 *tabIe[F] ポインタの F 要素の配列 ta e ANSI C : more 109

8. 月刊 C MAGAZINE 1991年6月号

関数を多重定義する場合は , ますプロト まず引数の数とデータ型が一致するもの て、は一致せず関数の選択は行われない。ポ タイプ宣言を行う。そこて、 , 引数の型ある が選択される。一致する関数がない場合は , インタにおいては , 仮引数が void 型へのポ いは数が異なっていなければならない。引 暗黙の型変換を行いデータ型が一致する関 インタと一致する。また , 実引数 0 は , あら 数が選択される。型変換て、は , char, unsigned 数の型と数が同じて、返り値が異なっている ゆるポインタ型の仮引数と一致する。 または short が int に , int は long および double ものはエラーとなる。 以上の暗黙の型変換により , 一致する関 に変換される。また float は double に変換さ 従来は , 多重定義する関数に対してオー 数が選択され , 関数の呼び出しが行われる。 れる。 ノヾロート . 目 . 日 , overload 関数名 , ただし , 実引数 ( 関数を呼び出す場合に指 今回は , 第 1 回に引きつづき , C 十十の基 を行わなければならなかったが , 現在はこ 定される引数 ) が int て、あるとき , 仮引数 ( プ 本機能となる制御構造 , 関数の定義 , 呼び ロトタイプ宣言て、の引数の型 ) が char や short の宣言は省略て、きる。 出し , オーバロードについて解説した。次 多重定義する関数の定義は通常の関数と のものとは一致しない。つまり ,char や short 回も引きつづき , C 十十の基本機能として , から int への変換は行われるが , その逆はな 同様に定義する。 変数の記憶クラス , プリプロセッサとイン また , 多重定義された関数の呼び出しに ライン関数 , ポインタについて解説する予 おいて , 呼び出される関数は以下のように したがって , 実引数が doblue の関数呼び 定て、ある。 出しと仮引数が float の多重定義された関数 して決定される。 実践編 文 生ロ 本 山 白 ンタの初期化忘れ」があります。 C プログラ 簡単な例をあげましよう。 MS-DOS のフ C は舗装道路 ァイルをオープンして , 1 行ずつ読み込んて、 マとしての経験を積むうちに , ポインタの くる処理て、す ( List 1 ) 。 扱いには細心の注意を払うようになります 変数 buff の宣言と初期化は離れた場所に 「 C プログラミングのテクニック」という表 が , それて、もポインタに限らず初期化がら あります。この例は切り詰められているか 現があります。しかし , テクニックやコツ みのミスを完全には払拭て、きないのが現実 らそうて、もないものの , 実際のコーディン というのは基本的には、、仕方ない使う便法 て、す。 て、す。道が曲がりくねっていたり , 障害物 があったりするからこそステアリングテク ニックを駆使しないといけないのて、あって , 直線道路には小手先の技が入りこむ余地は ありません。 C 十十の出発点は , C 言語の道にあった難 所を舗装するところからはじまっています。 どういう難所がどう舗装されるのか。今回 はスタートラインということて、 , 例を二つ ほどあげて解説します。 「とマでも宣言文」と仲間たち C 言語の初心者がよく陥るミスに , 「ポイ List main(int argc, char** argv) FILE *fp; char *buff; fp = fopen(argvC1], if ()U しし = fp) error ( ) : buff = mal 10C (BUFSIZ) : if (NUL し = buff) error() : while (NUL し ! = fgets(buff, BUFSIZ, (p)) { 128 C MAGAZINE 1991 6

9. 月刊 C MAGAZINE 1991年6月号

これはどのような型へのポインタかとい うと , 先ほどの Table と同じ型へのポインタ て、ある。すなわちトータルとしていえば table は次のような型の変数て、ある。 tab 厄は「 int の引数をニつ受け取り , int を返す関数」へのポインタの F 要素の配 さてここまて、きたら , 次には ( プログラム の構造化という観点からみると退歩なのだ が ) 関数 showTabIe をなくして main の中て、展 開してしまうことを検討しよう。結論から いうと List 4 のように記述すればよいのて、あ List 4 て、注目したいのは printf のところて、 使用している table を用いた関数呼び出しの 形式て、ある。 printf("%4d", table[k](i, これは K & R の時代て、はコンパイルエラー てある ( 今て、も一部のコンパイラて、はこのよ うな記述を通さないものがあるかもしれな い。しかしそれは ANSI 準拠て、はないという ことて、ある ) 。伝統的な記法て、は , 次のよう に書かねばならなかった。 (*table[k])(i, printf("%4d" 一見してわかるように , List 4 の書き方は 関数へのポインタ変数に対してインディレ クション ( 単項の * の適用 ) を行わなくても よくなったことによって可能になったもの てある。通常の感覚て、は配列の添字づけの うに修正したプログラムを示す。 っていたところが getFunc(int n) となってい 直後に関数の引数を表す力ッコがきている ることと , table の場合には初期化子が与え List 5 の注目点は getFunc という関数の定 のは奇妙に思えるが , ANSI C 標準にきちん られていたところに関数の本体が与えられ 義と , その呼び出し部分て、ある。まず定義 と従っているコンパイラて、あれば , これは ている点て、ある。 table の場合には [ F ] がつ のほうから考えてみる。 getFunc の定義は次 正しいプログラムて、ある。 いていたために配列変数となり , getFunc は さて少し視点を変えて , table という配列 のようになっている。 (int n ) がついているために関数となる。すな の代わりに「どの関数を呼び出すべきかを決 int (*getFunc(int n))(int, int) わち getFunc の型を文章て、述べればこうな 定する関数」を定義してみよう。その場合に は「呼び出すべき関数へのポインタを返す関 getFunc は「 int の引数をニつ受け取り , 数」を作ればよい このようにすると最初に int を返す関数」へのポインタを返す関数 述べた「呼び出すべき関数を計算する」とい であり , その引数は int n である う目的に近づくことになる。 List 5 にそのよ 引数の値に応じて呼び出すべき関数へのポインタを返す関数 LiSt 1 : / * switch を用いて分岐させたバージョン 2 : 3 : int (*getFunc(int n) ) (int, int) switch (n) { 5 : 6 : case 0 : return add; case 1 : 8 : 9 : return sub; case 2 : 11 : return mul; default: fprintf(stderr, "getFunc: is invalid argument*n ” , n) ; 13 : exit(l); 14 : 16 : } LiSt 1 : int 2 : min(void) static int (*tableCF]) (int, int) 4 : 5 : add, 6 : sub, mul, 8 : 9 : 10 : 11 : 14 : 15 : 19 : 20 : 22 : 23 : 24 : 25 : } / * 二つの int の引数を受け取り , int を返す関数へのポインタ f 皿 cp * / int (*funcp) (int, int) : int i, j, k; for k = 0 ; k く F; + + k ) { * k によって決定された呼び出すべき関数へのポインタを 内側のニつの for ループの外で funcp へとセットしておく * / funcp = tableCk] ; for (i = 1 ; i く N; + + i ) { for (j ー priÄtf{"hd" funcp(), j)) ; printf("}n ) ; printf( ”” ) ; return 0 ; これは , 先の table という配列の宣言と非 常によく似ている。違うのは table CF] とな 110 C MAGAZINE 1991 6

10. 月刊 C MAGAZINE 1991年6月号

浮動小数点型は計算精度などのいろいろ とやっかいな面が関わってきますのて , 別 の機会に詳しく説明することにしましよう。 さて今まて、なにげなく使ってきた 190.0 の ような数値を浮動小数点定数 ( floating con stat) と呼びます。浮動小数点定数に対して は , float 型を指定するために f または F の浮 動小数点接尾子 (floating suffix) を , long double 型て、あることを指定するために 1 また は L の浮動小数点接尾子をつけてもよい になっています。たとえば , ・ double 型 80.0 80 . 0f ・・ float 型 80.01 ・・ long double 型 となります。また数学的な表記が可能とな るように , 指数をつけて , 80. OE ー 5 / * 80.0X10 ー 5 の意味 * / のような記述をすることもて、きます。整数 部や小数点部を省略して , / * 0.5 の意味 * / / * 10.0 の意味 * / 10 . のような記述もて、きます。ただしすべての 部分を勝手に省略していいというわけて、は ありません。このような規則をまとめたの が Fig. 9 て、す。 大きな数値を表示するプログラム List く stdio. h> 1 : #include 2 : 3 : int main(void) long 5 : 6 : for (i = 32000L : i く 50000 し : i + + ) 7 : printf("%81d" 8 : return ( 0 ) : 9 : 10 : } scanf 標準入力 ( stdin ) から書式 format にしたがって , 引数のさすデータに値を格納していきま す。格納した入力フィールドの数を返します。格納されたフィールドがなければ 0 を返しま す。ファイルの終わりを読み込んだ場合には EOF を返します。 書式文字列は , ホワイトスペース ( 空白 , タブ \ t , 改行 \ n ) , 非ホワイトスペース ( % 文字以 外の文字 ) , および変換指定子からなります。書式文字列中の変換指定子と引数は , それぞれ の型と数が一致していなければなりません。 [ ホワイトスペース ] 書式文字列の中にホワイトスペースが現れると , 入力中の次の非ホワイトスペースまです べてのホワイトスペースを読みとばします。 [ 非ホワイトスペース ] 書式文字列の中に非ホワイトスペースが現れると , その文字と同じ非ホワイトスペースを 読みとばします。 [ 変換指定子 ] それぞれの書式指定子は , ' % ' から変換指定子までです。書式指定子の構文図は以下のよう になります。 intf と scanf List 4 のもうひとつの問題点は , 表示が見 にくいことて、した。整った表示をするため には , 8 桁の幅て、出力するなどの指定をする 必要があります。これまて、は整数 ( int ) の書式 指定には % d , 浮動小数点 ( float ) の書式指定 には % f を使ってきました。これらの書式指 定はごく一部て、あり , printf には多くの書式 設定の機能があります。 たとえば int 型の整数 x を 8 桁の幅て、出力 ( 表 示 ) するのを指定するには , priMf("%8d" とします。また printf を使用するときは , 引 数の型にも注意しなければなりません。た とえば % d は int 型を 10 進数て、表示することを 指定するものてあり , long 型の表示はて、き 122 C MAGAZINE 1991 6 変換指定子 代入制御文字 変換修飾子 幅指定子 【代入制御文字】 ・ * がある書式指定子に対応する入力フィールドを読みとばす これによりデータは代入されない 【幅指定子】 ・読み込まれる最大文字数を指定する n ホワイトスペースは文字数に含まれない