Download presentation
Presentation is loading. Please wait.
Published byしらん かくはり Modified 約 8 年前
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) 問題 ファイルを入力にとり,先頭に行番号をつけて出力でき る? 標準入力(キーボードなど)からの入力に対して,先頭に 行番号を右揃えでつけて出力できる?
Similar presentations
© 2024 slidesplayer.net Inc.
All rights reserved.