情報処理Ⅱ 第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) オブジェクト形式マクロとほぼ同じ書式. 置換対象に「(…)」をつける. このxは変数ではない! #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)) としなければならない. 関数形式マクロ(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 は文法上問題ないが,よい 書き方ではない.より好ましい例: schar型を新たに定義する. 意味は,signed charと同じ. 同一内容であれば,同じ名前のマクロを複数定義し てもよい.(異なっていればコンパイルエラー.) 置換内容のない名前も定義できる. #define schar signed char typedef signed char schar; #define DEBUG 末尾にセミコロンをつけない. #define WORD_SIZE 6; は(たいていの場合)間違い. 例題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 定数式(前処理時に評価できる式)が真のときに, 「…」を残し,そうでなければ「…」を捨てる. 条件付きコンパイル(2) 「#if 定数式」に代えて,「#ifdef 名前」や「#ifndef 名前」も利用可能. 「#else」や「#elif 定数式」も記述可能. 条件付きコンパイルの基本 参考: Cのif文 #if 条件式1 … #elif 条件式2 … #else … #endif if (条件式1) { … } else if (条件式2) { … } else { … } 条件付きコンパイルは入れ子にできる. 前処理指令と空白・コメント ...空白は必須 # define printfint( x ) printf ( #x " = %d\n" , x ) ...空白は任意 一つの前処理指令は,1行で書かなければならない. ただし, 行末に「\」を置くことで,複数行で書ける. 関数形式マクロの場合,括弧の途中で改行できる. 前処理指令の中でコメント(/* */ もしくは //)を書 くことができる.これは前処理時に空白文字に置き 換えられる. 有用なライブラリ関数(1) #include <stdio.h> を必要とするもの #include <stdlib.h> を必要とするもの putchar(c) … 1文字出力 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でもときどき見かける.
© Copyright 2024 ExpyDoc