250 likes | 343 Views
情報処理 Ⅱ. 第9回:2003年12月16日(火). 今回からのルール. 構文は, このフォント 使用例は, このフォント. 本日のテーマ. 前処理( preprocessing) # から始まる構文 代表的なライブラリ関数 ライブラリ関数の名前がわかれば,それを自分で使えるようになる!. 問題(授業がつまらない人の ために). 以下の DEBUGPRINT はどういう意味か? そうであることをどのように確認すればよいか? 配布資料のプログラムは仕様通りに動かない.どこをどのように修正すればよいか?. DEBUGPRINT printf("!<br>");.
E N D
情報処理Ⅱ 第9回:2003年12月16日(火)
今回からのルール • 構文は,このフォント • 使用例は,このフォント
本日のテーマ • 前処理(preprocessing) • # から始まる構文 • 代表的なライブラリ関数 • ライブラリ関数の名前がわかれば,それを自分で使えるようになる!
問題(授業がつまらない人のために) • 以下のDEBUGPRINTはどういう意味か? そうであることをどのように確認すればよいか? • 配布資料のプログラムは仕様通りに動かない.どこをどのように修正すればよいか? DEBUGPRINT printf("!\n");
前処理とコンパイル(1) • 前処理・コンパイル・アセンブル・リンクの各処理は通常,コンパイラ(ccなど)が一手に引き受ける. ソースファイル(前処理前) ソースファイル(前処理後) 前処理 コンパイルアセンブル オブジェクトファイル 実行ファイル リンク
前処理とコンパイル(2) • 前処理は, • 狭義には,「コンパイルに先立って行われる処理」であり,したがってコンパイルとは別 • 広義には,ccでコンパイルすれば自動的に処理してくれる,という意味でコンパイル作業の一部 • 前処理のコマンド(プリプロセッサ)はcpp • Cの前処理以外にも使用可能
前処理指令(Preprocessing directive) • マクロ定義(#define) • オブジェクト形式マクロ ⇒「定数」の定義 • 関数形式マクロ ⇒「関数もどき」の定義 • マクロ定義無効(#undef) • ソースファイルの取り込み(#include) • 条件付きコンパイル(#if ... #endif)
オブジェクト形式マクロ(1) • 語の置き換えを行う. • #define 置換対象 置換内容 • #define WORD_SIZE 6と記述すると,それ以降int a[WORD_SIZE];は int a[6];と同じ意味になる. • プログラム修正により変わり得る定数値があるときに,よく用いられる. • 配列の上限値,他と区別する値など. • うまく使うことで,定数値を変えるときのプログラム修正箇所を少なくできる.
オブジェクト形式マクロ(2) • 注意点 • 単純に置き換える. • #define WORD_SIZE 6+1に対してint a[WORD_SIZE * 2];は,int a[6+1 * 2];に置き換えられる(意図した動作ではない). • #define WORD_SIZE (6+1)とすればよい. • 語のみを置き換える. • print_WORD_SIZE( )といった「語の一部」や,printf("WORD_SIZE");といった文字列定数は,置き換えない.
関数形式マクロ(1) • オブジェクト形式マクロとほぼ同じ書式.置換対象に「(…)」をつける. • #define printfint(x) printf("%d\n",x)に対して,printfint(a);は printf("%d\n", a);に置き換えられる. • 注意すべき使用例 • #define add(x, y) x+yに対して,printf("%d", add(1,-1));やprintf("%d", !add(1,1));は意図通りに動作しない. • #define add(x, y) ((x)+(y))としなければならない. このxは変数ではない!
関数形式マクロ(2) • 置換内容の中で #を用いると,置換対象を文字列にできる. • #define printfint(x) printf(#x " = %d\n", x)に対してprintfint(a);は printf("a"" = %d\n", a);に置き換えられる(これは printf("a = %d\n", a);と同じ). • ##を用いると,前後の語を連結できる. • #define N1 100#define N2 1000#define N(i) N##iに対して,N(2) は,N2 に,そして 1000に置き換えられる. 順に変換(展開)される.
マクロ利用時の注意点 • C言語のキーワードも置換可能. • #define char signed char は文法上問題ないが,よい書き方ではない.より好ましい例: • #define schar signed char • typedef signed char schar; • 同一内容であれば,同じ名前のマクロを複数定義してもよい.(異なっていればコンパイルエラー.) • 置換内容のない名前も定義できる. • #define DEBUG • 末尾にセミコロンをつけない. • #define WORD_SIZE 6;は(たいていの場合)間違い. schar型を新たに定義する.意味は,signed charと同じ.
例題1(デバッグ) • うまく動かない. • 関数形式マクロを定義して,途中の値を出力するよう埋め込む.
ソースファイルの取り込み • #include <ファイル名> • ライブラリ関数などが宣言されているヘッダファイルを取り込む(インクルードする). • #include "ファイル名" • 自作のファイルを取り込む.
ヘッダファイル • ライブラリ関数の関数プロトタイプ,構造体や特殊な型,定数などが宣言・定義されているファイル. • #include <stdlib.h>とすると,/usr/include/stdlib.h を取り込む(ヘッダファイルの所在は処理系依存). • 自作するときは,ファイル名を「.h」で終わらせるのが慣例.
ヘッダファイルとライブラリ関数 • 既に定義されている関数や定数を利用するには,あらかじめ,適切なヘッダファイルをインクルードしなければならない. • printf なら #include <stdio.h> • NULL なら #include <stdlib.h>が一般的. • インクルードすべきヘッダファイル名は,manpage で知ることができる. • man 3 printf • jman 3 printf • JM Project (http://www.linux.or.jp/JM/)
例題1(改良) • ライブラリ関数 isdigit を使用して,文字が数字('0'~'9' のいずれか)か否かを判定する. • 定数や関数形式マクロを定義して,値の意味や処理の内容をわかりやすくする.
条件付きコンパイル(1) • #if 定数式…#endif定数式(前処理時に評価できる式)が真のときに,「…」を残し,そうでなければ「…」を捨てる.
条件付きコンパイルの基本 参考: Cのif文 if (条件式1) { … } else if (条件式2) { … } else { … } #if 条件式1 … #elif 条件式2 … #else … #endif 条件付きコンパイル(2) • 「#if 定数式」に代えて,「#ifdef 名前」や「#ifndef 名前」も利用可能. • 「#else」や「#elif 定数式」も記述可能. • 条件付きコンパイルは入れ子にできる.
前処理指令と空白・コメント • 一つの前処理指令は,1行で書かなければならない.ただし, • 行末に「\」を置くことで,複数行で書ける. • 関数形式マクロの場合,括弧の途中で改行できる. • 前処理指令の中でコメント(/* */ もしくは //)を書くことができる.これは前処理時に空白文字に置き換えられる. ...空白は必須 # define printfint( x ) printf ( #x " = %d\n" , x ) ...空白は任意
有用なライブラリ関数(1) • #include <stdio.h>を必要とするもの • putchar(c) … 1文字出力 • #include <stdlib.h>を必要とするもの • atoi(str) … 文字列からint型数値への変換 • exit(0) … プログラムの終了 • rand( ) … 乱数生成 • #include <string.h>を必要とするもの • strcmp(str1, str2) … 2つの文字列を比較 • strchr(str, c) … 文字検索
有用なライブラリ関数(2) • #include <ctype.h>を必要とするもの • isdigit(c) ... 文字が数字であるか判定 • tolower(c) … 大文字を小文字に変換 • #include <math.h>を必要とするもの • exp(x) … eのx乗 • floor(x) ... x以下で最大の整数(反対は ceil)
まとめ • 前処理指令をうまく使えば,人間にとって読みやすいプログラムを書くことができる. • 定数・関数マクロの定義 (#define) • 条件付きコンパイル (#if) • ライブラリ関数を使うには,#includeを用いて適切なヘッダファイルをインクルードする.
今後の予定 • 2004年1月13日(火):第10回 • ストリーム,特に標準入力と標準出力について • 落ち穂拾い • 2004年1月20日(火):おさらい • 2004年1月27日(火):試験 • 2004年2月3日(火)に補講(プログラミング能率向上のノウハウ;採点対象外)をすれば出席する人はどのくらいいますか?
発展的な話題:関数名の表記法 • printf • 最も単純な表記法. • ただし,コマンド名と一致するケースがある. • printf() • 関数呼び出しであることを明示する表記法. • printf(3) • 「詳細を知りたければ,man 3 printfを実行して読むこと」を含めた表記法. • manpageはこの形式.Webでもときどき見かける.