情報処理Ⅱ

情報処理Ⅱ
第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でもときどき見かける.