Presentation is loading. Please wait.

Presentation is loading. Please wait.

情報処理Ⅱ 第12回 2005 年 01 月 18 日(火). 2 本日学ぶこと 前処理指令 関数プロトタイプ ライブラリ関数の活用 関数は  すでにあるものを使うか?  関数として定義するか?  関数形式マクロとして定義するか?

Similar presentations


Presentation on theme: "情報処理Ⅱ 第12回 2005 年 01 月 18 日(火). 2 本日学ぶこと 前処理指令 関数プロトタイプ ライブラリ関数の活用 関数は  すでにあるものを使うか?  関数として定義するか?  関数形式マクロとして定義するか?"— Presentation transcript:

1 情報処理Ⅱ 第12回 2005 年 01 月 18 日(火)

2 2 本日学ぶこと 前処理指令 関数プロトタイプ ライブラリ関数の活用 関数は  すでにあるものを使うか?  関数として定義するか?  関数形式マクロとして定義するか?

3 3 前処理とコンパイル(1) ソースファイル (前処理前) ソースファイル (前処理後) オブジェクト ファイル 実行ファイル 前処理 リンク コンパイル アセンブル 前処理・コンパイル・アセンブル・リンクの各処理は通 常,コンパイラ (cc など ) が一手に引き受ける.

4 4 前処理とコンパイル(2) 前処理は,  狭義には,「コンパイルに先立って行われる処理」であり, したがってコンパイルとは別  広義には, cc でコンパイルすれば自動的に処理してくれ る,という意味でコンパイル作業の一部 前処理のコマンド(プリプロセッサ)は, cpp  C の前処理以外にも使用可能

5 5 前処理指令 (Preprocessing directive) #define マクロ定義 (#define)  オブジェクト形式マクロ ⇒「定数」の定義  関数形式マクロ ⇒「関数もどき」の定義 #include ソースファイルの取り込み (#include) #if#endif 条件付きコンパイル (#if... #endif など ) 「プリプロセッサ 指令」ともいう

6 6 列挙型のほうがいいかも オブジェクト形式マクロ(1) 語の置き換えを行う.  #define 置換対象 置換内容  #define WORD_SIZE 6 int a[WORD_SIZE];int a[6];  #define WORD_SIZE 6 と記述すると,それ以降 int a[WORD_SIZE]; は int a[6]; と同じ意味にな る. プログラム修正により変わり得る定数値があるときに, よく用いられる.  配列の上限値,他と区別する値など.  うまく使うことで,定数値を変えるときのプログラム修正 箇所を少なくできる.

7 7 オブジェクト形式マクロ(2) 注意点 #define WORD_SIZE 6+1  前提: #define WORD_SIZE 6+1  単純に置き換える. int a[WORD_SIZE * 2];int a[6+1 * 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");print_WORD_SIZE( ) といった「語の一部」や, printf("WORD_SIZE"); といった文字列定数は,置 き換えない.

8 8 オブジェクト形式マクロ(3) 注意点(続き)  予約語も置換可能. #define char signed char typedef signed char schar;#define char signed char は文法上問題ないが, よい書き方ではない.現在では, typedef signed char schar; とすべきである.  同一内容であれば,同じ名前のマクロを複数定義してもよ い.(異なっていればコンパイルエラー.)  置換内容のない名前も定義できる. #define DEBUG#define DEBUG  末尾にセミコロンをつけない. #define WORD_SIZE 6;#define WORD_SIZE 6; は(たいていの場合)間違 い.

9 9 関数形式マクロ(1) () オブジェクト形式マクロとほぼ同じ書式. 置換対象に「 (…) 」をつける.  #define pint(x) printf("%d\n",x) pint(a+1);printf("%d\n", a+1);  #define pint(x) printf("%d\n",x) に対して, pint(a+1); は printf("%d\n", a+1); に置き換え られる.  複数の引数をとることもできる.そのときは,置換対象の 各引数の間にカンマを入れる.  カッコ内に何も書かなければ,引数なしの関数形式マクロ が定義される.

10 10 関数形式マクロ(2) 注意点  単純に置き換える. #define mul(x, y) x*y z=mul(6+1,2); #define mul(x, y) ((x)*(y))#define mul(x, y) x*y に対して, z=mul(6+1,2); としたとき, z=14 ではなく z=8 とな る. ⇒ #define mul(x, y) ((x)*(y)) のように,置 換内容の引数と,置換内容全体にカッコをつける.  置換対象に引数を2箇所以上書くことができる.このとき, その回数だけ置換される. #define triple(x) ((x)+(x)+(x)) b=triple(++a); b=((++a)+(++a)+(++a));#define triple(x) ((x)+(x)+(x)) に対して b=triple(++a); と書くと, b=((++a)+(++a)+(++a)); となる.

11 11 関数形式マクロ(3) # 引数 置換内容の中で「 # 引数」と書くと,引数を文字列にで きる.  #define pint(x) printf(#x " = %d\n", x) pint(a+1);printf("a+1" " = %d\n", a+1);  #define pint(x) printf(#x " = %d\n", x) に対し て pint(a+1); は printf("a+1" " = %d\n", a+1); に置き換えられる.  通常の関数定義では,変数名を 引数にとってその文字列を得る ことはできない. 「文字列リテラルの連 結」により,これは printf("a+1 = %d\n", a+1); と同じとなる.

12 12 関数か関数形式マクロか 関数 … 「機能」を正確に表現したいとき int square_int(int x) { return x * x; }  例: int square_int(int x) { return x * x; }  引数や戻り値の型に制約される.  関数呼び出しのオーバーヘッドがある.  ローカル変数や制御文を活用できる.  実引数が ++a などのときも,その評価は一度だけ. マクロ … 「機能」を簡便に表現したいとき #define square_int(x) ((x) * (x))  例: #define square_int(x) ((x) * (x))  引数や評価式に型はない.  (狭義の)コンパイル前に展開され,オーバーヘッドは少 ない.  ローカル変数や制御文は使用しにくい.  (マクロ利用側の)引数は,置換内容の回数だけ評価され る.

13 13 前処理指令と空白・コメント 一つの前処理指令は,1行で書かなければならない.た だし,  行末に「 \ 」を置くことで,複数行で書ける.  関数形式マクロの場合,括弧の途中で改行できる. /* */// 前処理指令の中でコメント( /* */ もしくは //) を 書くと,前処理時に空白文字に置き換えられる. # define pint( x ) printf ( #x " = %d\n", x )... 必須... 任意... 不可

14 14 条件付きコンパイル(1) #if 定数式 #endif #if 定数式 … #endif  定数式が真のときに「 … 」を残し,そうでなければ「 … 」 を捨てる.  定数式の評価や「 … 」の取捨は,前処理時に行われる.

15 15 条件付きコンパイル(2) #if 定数式 #ifdef 名前 #ifndef 名前 「 #if 定数式」に代えて,「 #ifdef 名前」や 「 #ifndef 名前」も利用可能. #else#elif 定数式 「 #else 」や「 #elif 定数式」も記述可能. 条件付きコンパイルは入れ子にできる. if ( 条件式 1) { … } else if ( 条件式 2) { … } else { … } 参考 : C の if 文 #if 条件式 1 … #elif 条件式 2 … #else … #endif 条件付きコンパイル

16 16 他のファイルの取り込み #include #include  ライブラリ関数などが宣言されているファイルを取り込む (インクルードする). #include " ファイル名 " #include " ファイル名 "  自作のファイルを取り込む.

17 17 ヘッダファイル 関数プロトタイプ,構造体や特殊な型,定数などが宣 言・定義されているファイル.  #include  #include とすると, /usr/include/stdlib.h を取り込む(ヘッダファイ ルの所在は処理系依存).  ヘッダファイルの中で,他のヘッダファイルをインクルー ドすることもよく行われる. 慣例として  ファイル名を「.h 」で終わらせる.  関数は,「宣言」のみして「定義」はしない.  同一ファイルに対する複数回のインクルードがあっても, 2回目以降は処理しないようにする.

18 18 関数プロトタイプ (Function prototype) 型名 関数名 ( 引数の型の並び ); 構文 : 型名 関数名 ( 引数の型の並び );  「引数の型の並び」は,「引数(型と変数)の並び」でも よい.このとき変数名は無視される.  一般に,グローバル区間に記述する. int swapcase(int);  例 : int swapcase(int); 関数プロトタイプを用いることで, 順番を気にすることなく  関数定義の順番を気にすることなくプログラムを記述でき る. ただし,関数プロトタイプは,関数定義の前に書くこ と.  関数の入出力が明確になる. セミコロンを忘れず に 「関数原型」 ともいう

19 19 ヘッダファイルとライブラリ関数 既に定義されている関数や定数を利用するには,あらか じめ,適切なヘッダファイルをインクルードしなければ ならない.  printf#include  printf なら #include  NULL#include  NULL なら #include が一般的. インクルードすべきヘッダファイル名は, manpage で 知ることができる.  man 3 printf  jman 3 printf  JM Project (http://www.linux.or.jp/JM/)

20 20 英字大小変換プログラム(1) 仕様  コマンドライン引数の各語の英字の大小を変換する. 例 ./upcase Wakayama Univ. WAKAYAMA UNIV. ./downcase Wakayama Univ. wakayama univ. ./swapcase Wakayama Univ. wAKAYAMA uNIV. 一つのプログラムファイル (upcase.c) から,3つの実 行ファイル (upcase,downcase,swapcase) を作る.

21 21 英字大小変換プログラム(2) 1文字ごとの変換  ライブラリ関数を使用する. tolower 大 → 小 : tolower toupper 小 → 大 : toupper  関数形式マクロを用いて upcase と downcase も定義して おく.  swapcase は関数で定義するのが自然. 文字列ごとの変換  main 関数の中で,2重ループにより処理する. 外側の for は,コマンドライン引数を順番に見る. 内側の while は,文字列を 1 文字ずつ見る. これらは自作しよう と思わないこと

22 22 英字大小変換プログラム(3) 「ひとつのプログラムファイルから,3つの実行ファイ ルを作る」方法  make upcase  ln -s upcase downcase  ln -s upcase swapcase コマンド名に特定の文字列が含まれていれば, upcase 関数に代えて, downcase または swapcase の関数を呼 び出す. argv[0]  コマンド名は, argv[0] . strstr  文字列が含まれているかのチェックには,ライブラリ関数 の strstr を用いる.ここでは has_string 関数を(関数 形式マクロで)定義している. ファイル downcase が作 られ,これはファイル upcase のシンボリックリ ンクとなる.

23 23 有用なライブラリ関数(1) #include #include を必要とするもの putchar  int putchar(int c) … 1 文字出力 #include #include を必要とするもの atoi  int atoi(char *s) … 文字列から整数値への変換 exit  void exit(int status) … プログラムの終了 rand  int rand(void) … 乱数生成 #include #include を必要とするもの strlen  size_t strlen(char *s) … 文字列の長さ strcmp  int strcmp(char *s1, char *s2) … 2つの文字列を比 較 strstr  char *strstr(char *s1, char *s2) … 文字列検索

24 24 有用なライブラリ関数(2) #include #include を必要とするもの isdigit  int isdigit(int c) … 文字が数字であるか判定 tolower  int tolower(int c) … 大文字を小文字に変換 toupper  int toupper(int c) … 小文字を大文字に変換 #include #include を必要とするもの exp  double exp(double x) … e の x 乗 floor  double floor(double x) … x 以下で最大の整数

25 25 まとめ 前処理指令と関数プロトタイプをうまく使えば,人間に とって読みやすいプログラムを書くことができる. ライブラリ関数を使うには, #include を用いて適切な ヘッダファイルをインクルードする. 前処理は,コンパイルの前に行われる.そのため書式は C の文法と異なる.

26 26 次に学ぶこと:ファイル処理(1) UNIX の中での「ファイル」  UNIX では,すべての入出力はファイルを読み書きするこ とによって行われる.キーボードや画像でさえも,ファイ ルシステム上のファイルである.(『プログラミング言語 C 』一部改変) C プログラミングの中での「ファイル」  プログラムが終了しても,内容が保持されるデータ構造. 比較:オブジェクトは, auto 変数, static 変数, ヒープ領域の変数のいずれも,プログラム終了時に破 棄される. 「ストリーム」を介して,バイト列として読み書き可 能.

27 27 次に学ぶこと:ファイル処理(2) 問題  ファイルを入力にとり,先頭に行番号をつけて出力でき る?  標準入力(キーボードなど)からの入力に対して,先頭に 行番号を右揃えでつけて出力できる?


Download ppt "情報処理Ⅱ 第12回 2005 年 01 月 18 日(火). 2 本日学ぶこと 前処理指令 関数プロトタイプ ライブラリ関数の活用 関数は  すでにあるものを使うか?  関数として定義するか?  関数形式マクロとして定義するか?"

Similar presentations


Ads by Google