2章
2.1~2.9
2.10~2.19
2.20
問題:
(1)文字コードの値について不確かな仮定を行っている式を探せ。
(2)JavaのCharacterクラスのisUpperメソッドかtoLowerCaseメソッド、
あるいはC言語のctype.h内のisupper()かtolower()を読め。
(3)ターゲットアーキテクチャの文字集合に対する依存度を下げるために何が
必要かを提案せよ。
解答:探すのが大変だったが以下の箇所に発見。
hengband-1.7.0/src/autopick.c
l4334
if ('a' <= key && key <= 'z')
本文中にあるように、これはASCII文字を前提として書かれている。
もしa~zの範囲外に小文字があったり、あるいはa~zの範囲内に小文字以外の
文字が存在した場合はプログラムは期待通りに動かないだろう。
(2)/usr/include/ctype.hを覗いてみる。
l182
# define isupper(c) __isctype((c), _ISupper)
l88~89 __isctype()
#define __isctype(c, type) \ ((*__ctype_b_loc ())[(int) (c)] & (unsigned short int) type)
l48~50 _ISupper
enum { _ISupper = _ISbit (0), /* UPPERCASE. */
l81~82
extern __const unsigned short int **__ctype_b_loc (void) __attribute__ ((__const));
l43
# define _ISbit(bit) (1 << (bit))
構造が複雑なのでまとめてみた。
- isupper()マクロ
- __isctype()マクロ
- __ctype_b_loc()関数
- _ISupper()マクロ
- _ISbit()マクロ
- __isctype()マクロ
ここまでを見る限り、実体が__ctype_b_loc()関数であることがわかる。
これは/usr/lib/libc.a内に含まれているので、とりあえず読んでみることにしよう。
読めればだけど。
結局__ctype_b_loc()の実装はglibcのソースにもなかったので、webで検索。
http://refspecs.freestandards.org/LSB_3.0.0/LSB-Core-generic/LSB-Core-generic/baselib---ctype-b-loc.html
ここによると、現在のロケールのキャラクタ配列へのポインタを返すらしい。
だから__isctype()の前半は配列になっているわけだ。
(3)一つ一つ文字チェックするしかないんじゃない?
まあそれは現実的でないとしても、アーキテクチャに依存する処理をまとめて一つの関数にしておけば
移植の容易性は上がるとは思う。
2.21
問題:ソースコードから自明でないブール式を5つ探し出し、式の真偽を決める条件を推論せよ。
短絡評価法を積極的に活用せよ。
解答:自明でないブール式を探すのが難しい。
(08/06/04追記:読み返したらブール式でなく整数式を引用していたので2.22に移動した)
とりあえず一つだけ。
postfix-2.3.3/src/master/master_sig.c
l107-113
if (scp != NULL && scp->sc_syscall == SYS_select) { scp->sc_syscall_action = SIG_RETURN; #ifndef SA_RESTART } else if (scp != NULL) { scp->sc_syscall_action = SIG_RESTART; #endif }
おそらくは構造体なのだろう、scpのメンバにアクセスして判定を行う前に
scp自体が有効なアドレスを参照しているかどうかをチェックしている。
ちなみにこの箇所が含まれている関数の引数が以下のようになっている。
postfix-2.3.3/src/master/master_sig.c
l96
static void master_sigchld(int sig, int code, struct sigcontext * scp)
先ほどの式が有効かどうかはこの sigcontext 構造体を調べればよい。
2.22
問題:ソースコードから自明でない文字式または整数式を5つ探し出し、式の真偽を決める条件を推論せよ。
できるだけ少ないコードで推論せよ。
解答:
とりあえず1つだけ。
tar-1.17-3.fc8/tar-1.17/src/create.c
l629
if (i == 0 || length - i - 1 > NAME_FIELD_SIZE)
単純に読めば、iが0以外で、length以下が規定のサイズより大きいことが条件となる。
で、このiは、
i = split_long_name (name, length);
を見ると、lengthと関係のある値ということがわかる。
次にlengthを見ると、
static union block * write_ustar_long_name (const char *name) { size_t length = strlen (name);
nameの長さということがわかる。
2.23
問題:
gotoステートメントを使用しているコードを5つ探しだし、用途別に分類しなさい(5例探しだす)。
それぞれのgotoを、ループステートメントに置き換えることができるか。またそれが適切か。
解答:
- CodeReading/netbsdsrc/bin/rcp/util.c
int okname(cp0) char *cp0; { int c; char *cp;
cp = cp0; do { c = *cp; if (c & 0200) goto bad; if (!isalpha(c) && !isdigit(c) && c != '_' && c != '-') goto bad; } while (*++cp); return (1);
bad: warnx("%s: invalid user name", cp0); return (0); }
例外処理としてgotoを使っている。
置き換えることができない。
- CodeReading/netbsdsrc/bin/ksh/alloc.c
/* allocate object from Area */ void * alloc(size, ap) size_t size; register Area *ap; { int cells, split; register Block *bp; register Cell *dp, *fp, *fpp;
if (size <= 0) { aerror(ap, "allocate bad size"); return NULL; } cells = (unsigned)(size - 1) / sizeof(Cell) + 1;
/* find Cell large enough */ for (bp = ap->freelist; ; bp = bp->next) { for (fpp = NULL, fp = bp->freelist; fp != bp->last; fpp = fp, fp = fpp->next) if ((fp-1)->size >= cells) goto Found;
/* wrapped around Block list, create new Block */ if (bp->next == ap->freelist) { bp = (Block*) malloc(offsetof(Block, cell[ICELLS]) + sizeof(bp->cell[0]) * cells); if (bp == NULL) { aerror(ap, "cannot allocate"); return NULL; } if (ap->freelist == &aempty) bp->next = bp; else { bp->next = ap->freelist->next; ap->freelist->next = bp; } bp->last = bp->cell + ICELLS + cells; fp = bp->freelist = bp->cell + 1; /* initial free list */ (fp-1)->size = ICELLS + cells - 1; fp->next = bp->last; fpp = NULL; break; } } Found: ap->freelist = bp; dp = fp; /* allocated object */ split = (dp-1)->size - cells; if (split < 0) aerror(ap, "allocated object too small"); if (--split <= 0) { /* allocate all */ fp = fp->next; } else { /* allocate head, free tail */ (fp-1)->size = cells; fp += cells + 1; (fp-1)->size = split; fp->next = dp->next; } if (fpp == NULL) bp->freelist = fp; else fpp->next = fp; return (void*) dp; }
break文の代わりにgoto使用している。breakに置き換えた方がいい。
2.24
問題:
関数getoptは、いくつかの異なるエラーで同じエラーメッセージを生成する。
このエラーレポートをユーザーフレンドリーなものとする一方で、gotoステートメントを省くにはどうしたらいいか。
素のような変更はどのようなケースに適していて、どんなケースで避けるべきか。
回答:
以下のようにエラーの種類ごとに出力するエラーメッセージを変える。
gotoステートメントを省くことができたが、同じ種類のエラーメッセージを出力するときに手間がかかる。
複数回つかう箇所はgotoを使うのが現実的。
getstops(cp) char *cp; { int i;
nstops = 0; for (;;) { i = 0; while (*cp >= '0' && *cp <= '9') i = i * 10 + *cp++ - '0'; if (i <= 0 || i > 256) { fprintf(stderr, "error1\n"); exit(1); } if (nstops > 0 && i <= tabstops[nstops-1]) fprintf(stderr, "error2\n"); exit(1); tabstops[nstops++] = i; if (*cp == 0) break; if (*cp != ',' && *cp != ' ') fprintf(stderr, "error3\n"); exit(1); cp++; } }