情報処理Ⅱ 2006年11月24日(金)
本日学ぶこと ポインタを使ったプログラミング 反復語判定の拡張版 対象語は,コマンドライン引数から獲得 文字列走査 ビットパターン出力 int型,float型の値に対して出力 ポインタ値の型変換 コマンドラインオプション
これまでの,補足(1) for文を「forループ」,while文を「whileループ」と呼ぶことがある. ループ = loop = 糸・ひもなどの輪 for文で計数に使用する変数を「ループカウンタ」という.
これまでの,補足(2) char *p = "abc"; としたとき,「文字列p」と書くことがある. 文字列が等しいかを比較するときに,演算子 == は使用できない. char *p = "abc"; char *q = "abc"; のとき, p == q は偽(異なるオブジェクトを指し示している)と考える char p[] = "abc"; char *q = "abc"; のとき, p == q は偽(異なるオブジェクトを指し示している) char p[] = "abc"; char *q = p; のとき, p == q は真(同じオブジェクトを指し示している) 文字列比較は,ライブラリ関数の strcmp を使用する
反復語判定にコマンドライン引数を 仕様 例 入力は,コマンドライン引数から獲得する. コマンドライン引数の各文字列について, 反復語(2回以上の繰り返し)になっていれば「Yes」を, そうでなければ「No」を出力する. 例 ./iterative2 muumuu MuuMuuMuu MuuM "muumuu" : Yes "MuuMuuMuu" : Yes "MuuM" : No
反復語判定:アイディア 反復語は,先頭何文字か(「部分文字列」とここでは言う)の繰り返しである プログラミングの 前に,ちょっと 検討する習慣を! 反復語判定:アイディア 反復語は,先頭何文字か(「部分文字列」とここでは言う)の繰り返しである ①部分文字列の候補を見つけ,②繰り返しになっているかを確認すればよい. 例1: m u u m u u m u u m u u 例2: a a b b a a b b 部分文字列の性質 その直後の文字が,先頭の文字と一致する. 反復語の長さは,部分文字列の長さで割り切れる. ある語に対して,部分文字列(の候補)は複数あり得る.
反復語判定(部分文字列の候補発見) 先頭から1文字ずつ見て, 部分文字列の候補を求める. p q argv[i] 'm' 'u' 'u' 1文字ずつ見ていく処理を 文字列の「走査(scan)」という 先頭から1文字ずつ見て, 部分文字列の候補を求める. 部分文字列は p から q - 1 までの文字配列 p q ヌル文字がない ので,実は文字列 ではない argv[i] 'm' 'u' 'u' 'm' 'u' 'u' '\0'
反復語判定(繰り返しの確認) 対応する文字を1文字ずつ見て比較する. p q argv[i] 'm' 'u' 'u' 'm' 'u' 'u' r が q まで来たら,文字列の先頭に戻る. 繰り返しになっていないか,sが文字列の末尾まで来たら終了. p q argv[i] 'm' 'u' 'u' 'm' 'u' 'u' '\0' 「 r が q まで来たら,文字列の先頭に戻る. 」は,実は不要 (反復語となる文字列の周期性に注意すると,r を s と同様に1増やしていき,減らさないことにしても,判定できる.) r s
反復語判定:ポインタ変数の役割 char *p = argv[i]; char *q; char *r; char *s; 対象文字列 部分文字列(の候補)の次の位置を指す. p から始まり,文字列走査により1ずつ増える. char *r; 繰り返し確認用 p から始まり,反復語であれば,部分文字列を (反復回数-1)回,巡回する char *s; q から始まり,1ずつ増える.
ビットパターン出力1 仕様 例 応用すれば,任意のアドレスの値を獲得したり,書き換えたりできる.(Cが適度に低水準な理由の一つ!) int型の値を,バイト単位で最上位ビットから最下位ビットの順に,2進数で出力する.バイト間には空白文字を置く. 入力は,プログラム内で指定する. 例 40 ⇒ 00101000 00000000 00000000 00000000 応用すれば,任意のアドレスの値を獲得したり,書き換えたりできる.(Cが適度に低水準な理由の一つ!) 40の2進表記 ??? 「任意のアドレスの値を獲得したり,書き換えたりできる」というのは実は嘘. オペレーティングシステムのメモリ保護機能により,書き換えられないアドレスを書き換えようとすると,エラーが発生し,プログラムは終了する.
ビットパターン出力1:アイディア int x = 40; char *p = (char *)&x; とすると,*p, *(p + 1), ..., *(p + sizeof(x) - 1) は, xの「バイトごとの」中身である. 1バイトのビットパターンを求めるプログラムを活用して, バイトごとに出力すればいい! int x : p p+1 p+2 p+3 char *p : はオブジェクト, は1バイト
ポインタ型のキャストが必要な理由 int x = 40; としたとき, p1+1 int *p1 : int x : p3+1 int *p1 = &x; としてもうまくいかない. char *p2 = &x; は,型が合わない. char *p3 = (char *)&x; とすればうまくいく. int *p1 : p1+1 は1バイト char *p3 : p3+1 int x :
ビットパターン出力2 仕様 int型のほか,float型の値を,バイト単位で最上位ビットから最下位ビットの順に,2進数で出力できるようにする. 入力は,コマンドラインから指定する.最初の1個の数値のみでよい.数値が指定されなければ,1とする. 数値の前にオプション(コマンドラインオプション)を指定可能 -f : float型の値を出力する(float) -r : バイト単位で逆順に出力する(reverse) -p : バイトごとにそのアドレスも出力する(pointer) 「-frp」のように一括指定も可能
ビットパターン出力2(続き) 実行例 ./intfloatbit ⇒ 00000001 00000000 00000000 00000000 ./intfloatbit -f 1 ⇒ 00000000 00000000 10000000 00111111 ./intfloatbit -f -r 1 ⇒ 00111111 10000000 00000000 00000000 ./intfloatbit -pr 1 ⇒ 0x22eec7:00000000 0x22eec6:00000000 0x22eec5:00000000 0x22eec4:00000001 コマンド名(必ず1つ) コマンドラインオプション(任意個) コマンドパラメータ(任意個) すべて合わせて「実行コマンド」
ビットパターン出力2:構成要素 コマンドラインオプションの獲得(コマンドライン解析)方法 intでもfloatでも出力できる方法 逆順の出力方法
バイトオーダ 実行例 値が「バイト単位で」メモリにどのように格納されるか(バイトオーダ)は,処理系により異なる. ./intfloatbit 1 ⇒ 00000001 00000000 00000000 00000000 ./intfloatbit -r 1 ⇒ 00000000 00000000 00000000 00000001 値が「バイト単位で」メモリにどのように格納されるか(バイトオーダ)は,処理系により異なる. 最下位バイトから最上位バイトの順に格納していくのを,リトルエンディアンという.演習室のLinux環境も該当する. 逆に,最上位バイトから最下位バイトの順に格納するのは,ビッグエンディアンという.ネットワークバイトオーダともいう.
この表現形式も,処理系に依存する(Cの規格 ではない)が,多くの処理系で採用されている float型のビットパターンの意味 ./intfloatbit -fr 1 ⇒ 00111111 10000000 00000000 00000000 ./intfloatbit -fr 2 ⇒ 01000000 00000000 00000000 00000000 ./intfloatbit -fr 3 ⇒ 01000000 01000000 00000000 00000000 ./intfloatbit -fr -3 ⇒ 11000000 01000000 00000000 00000000 全ビットが0のとき,値は0 NaNや無限大などの表現も考慮すると,符号のビットが0でも,値が正であるとは限らない. この形式は「IEEE 754形式」と呼ばれる. 符号(1bit) 0なら正 1なら負 指数部(8bit) 底は2,整数値 127を引けば 実際の指数になる. 仮数部(23bit) 2進数,小数点以下 1を加えれば,実際の仮数になる
まとめ int main(int argc, char *argv[])で,コマンドライン引数(文字列の配列)を獲得できる. argv[0]はコマンド名,argv[argc]はNULL 残りの文字列をどう扱うかは,プログラム内で決める 文字列の走査,バイト単位での処理には,char *型のポインタを活用するとよい. ポインタ値のキャスト(型変換)により,アドレスを変えることなく,任意のポインタ型に変換できる.