Post on 01-Jan-2016
description
確認問題 : ポインタのポインタ• *p, *pp, **pp はいくらか?
3
p
&a
pp
&p
0x22aab8 = &pp
0x22aac0 = &p
a
'h'
0x22aacc = &a
hoge.cint a = 'h'; // = 0x68 = 104
int *p = &a;int **pp = &p;
printf("&a = %p\n", &a);printf("&p = %p\n", &p);printf("&pp = %p\n", &pp);printf("sizeof(a) = %d\n", sizeof(a));printf("sizeof(p) = %d\n", sizeof(p));printf("sizeof(pp) = %d\n", sizeof(pp));
mintty + bash + GNU C $ gcc hoge.c && ./a&a = 0x22aacc&p = 0x22aac0&pp = 0x22aab8sizeof(a) = 4sizeof(p) = 8sizeof(pp) = 8
6789101112131415
確認問題 : ポインタのポインタ• pp + 1, *(pp + 1), pp[1] はいくらか?
4
p
&a
pp
&p
0x22aab8 = &pp
0x22aac0 = &p
a
'h'
0x22aacc = &a
hoge.cint a = 'h'; // = 0x68 = 104
int *p = &a;int **pp = &p;
printf("&a = %p\n", &a);printf("&p = %p\n", &p);printf("&pp = %p\n", &pp);printf("sizeof(a) = %d\n", sizeof(a));printf("sizeof(p) = %d\n", sizeof(p));printf("sizeof(pp) = %d\n", sizeof(pp));
mintty + bash + GNU C $ gcc hoge.c && ./a&a = 0x22aacc&p = 0x22aac0&pp = 0x22aab8sizeof(a) = 4sizeof(p) = 8sizeof(pp) = 8
6789101112131415
確認問題 : 配列と文字列• sizeof(s), strlen(s), sizeof(p), strlen(p), p[3] は
いくらか?
5
hoge.cchar s[] = "hello";
char *p = s;p++;
printf("sizeof(&s[0]) = %d\n", sizeof(&s[0]));
678910
s[0]
'h'
s[1]
'e'
s[2]
'l'
s[3]
'l'
s[4]
'o'
s[5]
'\0'
s[0]
0x68
s[1]
0x65
s[2]
0x6c
s[3]
0x6c
s[4]
0x6f
s[5]
0x00=
mintty + bash + GNU C $ gcc hoge.c && ./asizeof(&s[0]) = 8
総当たり探索• 先頭から 1 つ探索する方法• データが n 個ある時
• 最悪 n 個全て調べる必要がある• 見つかる場合の平均探索回数 n/2 回の確率
• 簡単だけど速くない
7
s[0]
2
s[1]
5
s[2]
7
s[3]
11
...
...
s[99]
541
541
...
総当たり探索• 簡単だけど遅い
8
asearchi_test.c int data[] = {
2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, }; int key; int *p;
89101112131415161718192021
asearchi_test.cp = asearchi(key, data, sizeof(data)/sizeof(int));
if (p) { printf("data[%d] = %d\n", p - data, *p);} else { printf("not found.\n");}
262728293031
asearchi.cint *asearchi(int key, int *data, int n)
{ int i; for (i = 0; i < n; i++) {#ifdef DEBUG printf("[%d] = %d\n", i, data[i]);#endif if (key == data[i]) { return &data[i]; } } return NULL;}
45678910111213141516
mintty + bash + GNU C$ gcc asearchi_test.c asearchi.c -DDEBUG && ./a
13key = ?[0] = 2[1] = 3[2] = 5[3] = 7[4] = 11[5] = 13data[5] = 13
演習 : asearchi.c
• int型のデータを総当たり探索する関数 asearchi を作成せよ
• asearchi_test.c と共にコンパイルして動作を確認せよ
• 引数• int key : 検索したい値へのポインタ• int *data : 検索対象のデータ集合へのポインタ• int n : データの要素数
• 戻り値• 見つけたデータへのポインタを返す• 見つからなかった場合はNULLを返す
9
mintty + bash + GNU C$ gcc asearchi_test.c asearchi.c && ./a
key = ? 31data[10] = 31
2 分探索• 昇順ソート済みのデータから探す• 探す領域を半分に探す範囲を絞り込む• データが n 個ある時
• 最悪個調べれば良い• 速いけど事前にソートが必要
10
s[0]
2
s[1]
3
s[2]
5
13
s[3]
7
s[4]
11
s[5]
13
s[6]
17
s[7]
19
s[8]
23
教科書 pp.198-202.
2 分探索• 全体を 2 つに分けて• 中央の値と比較して行く
11
s[0]
2
s[1]
3
s[2]
5
s[3]
7
s[4]
11
s[5]
13
s[6]
17
s[7]
19
s[8]
23
s[0]
2
s[1]
3
s[2]
5
s[3]
7
s[4]
11
s[5]
13
s[6]
17
s[7]
19
s[8]
23
s[0]
2
s[1]
3
s[2]
5
13
s[3]
7
s[4]
11
s[5]
13
s[6]
17
s[7]
19
s[8]
23
探索成功
教科書 pp.198-202.
2 分探索• 全体を 2 つに分けて• 中央の値と比較して行く
12
s[0]
2
s[1]
3
s[2]
5
s[3]
7
s[4]
11
s[5]
13
s[6]
17
s[7]
19
s[8]
23
s[0]
2
s[1]
3
s[2]
5
s[3]
7
s[4]
11
s[5]
13
s[6]
17
s[7]
19
s[8]
23
s[0]
2
s[1]
3
s[2]
5
18
s[3]
7
s[4]
11
s[5]
13
s[6]
17
s[7]
19
s[8]
23
探索失敗
教科書 pp.198-202.
2 分探索• 標準ライブラリ関数ライクな実装例
13教科書 pp.198-202.
bsearchi_test.c int data[] = {
2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, }; int key; int *p;
89101112131415161718192021
bsearchi_test.cp = bsearchi(key, data, sizeof(data)/sizeof(int));
if (p) { printf("data[%d] = %d\n", p - data, *p);} else { printf("not found.\n");}
262728293031
bsearchi.cint *bsearchi(int key, int *data, int n)
{ int cmp; if (n <= 0) return NULL;#ifdef DEBUG printf("%d: [%d] = %d\n", n, n/2, data[n/2]);#endif if (key < data[n/2]) { return bsearchi(key, data, n/2); } else if (data[n/2] < key) { return bsearchi(key, &data[n/2+1], n - n/2 - 1); } else { return &data[n/2]; }}
456789101112131415161718
mintty + bash + GNU C$ gcc bsearchi_test.c bsearchi.c -DDEBUG && ./a
key = ? 17100: [50] = 23350: [25] = 10125: [12] = 4112: [6] = 17data[6] = 17
演習 : bsearchi.c
• 昇順ソート済みのint型のデータを2分探索する関数 bsearchi を作成せよ
• bsearchi_test.c と共にコンパイルして動作を確認せよ
• 引数• int key : 検索したい値へのポインタ• int *data : 検索対象のデータ集合へのポインタ• int n : データの要素数
• 戻り値• 見つけたデータへのポインタを返す• 見つからなかった場合はNULLを返す
14
mintty + bash + GNU C$ gcc bsearchi_test.c bsearchi.c && ./a
key = ? 31data[10] = 31
標準ライブラリの 2 分探索関数• 標準ライブラリ関数• void *bsearch(const void *key, const void *base,
size_t n, size_t size, int (*cmp)(const void *keyval, const void *datum));
• 引数• key : 検索したい値へのポインタ• base : 検索対象のデータ集合へのポインタ• n : データの要素数• size : データの 1要素当りのバイト数• cmp : データ比較用の関数へのポインタ
• 戻り値• マッチした項目へのポインタを返す• マッチした項目がない場合 NULLを返す
15教科書 pp.198-202.
標準ライブラリの 2 分探索関数• 比較関数さえ用意すれば簡単に検索出来
る
16教科書 pp.198-202.
bsearch_test.cint cmp(const int *keyval, const int *datum)
{ return *keyval - *datum;}
4567
bsearch_test.cp = bsearch(&key, data, sizeof(data)/sizeof(int), sizeof(int), (int (*)(const void*, const void*))cmp);
if (p) { printf("data[%d] = %d\n", p - data, *p);} else { printf("not found.d\n");}
293031323334
bsearch_test.c{
int data[] = { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541,};int key;int *p;
111213141516171819202122232425
mintty + bash + GNU C$ gcc bsearch_test.c && ./a
key = ? 113data[29] = 113
関数へのポインタ• ポインタ変数に * が付いたら何になる
か?
17
関数へのポインタ変数の宣言int (*fnc)(int *a, int *b);
関数の定義int cmp(int *a, int *b){ return *a - *b;}
関数のプロトタイプ宣言int cmp(int *a, int *b);
fnc がポインタ変数で*fnc が戻り値が int 型引数が (int *a, int *b) の関数という意味になる
cmp が戻り値が int 型引数が (int *a, int *b) の関数という意味になる
関数へのポインタに代入と呼び出しfnc = cmp;(*fnc)(&x, &y);
*fnc とすれば関数へのポインタが関数になる演算子の優先順位は高 *>() 低なので *fnc に () が必要
関数へのポインタ• 関数関連の宣言では引数名は省略可能
• 関数のプロトタイプ宣言• 関数へのポインタ変数の宣言
18
関数へのポインタ変数の宣言
関数へのポインタ変数の宣言int (*fnc)(int *a, int *b);
関数のプロトタイプ宣言int cmp(int *a, int *b);
int (*fnc)(int * , int * );
関数のプロトタイプ宣言int cmp(int * , int * );
関数へのポインタ• 汎用型の場合キャストが必要
• 例 : qsort や bsearch へ渡す比較関数
19
関数へのポインタ変数の宣言int (*fnc)(void *a, void *b);
関数のプロトタイプ宣言 int cmp(int *a, int *b);
関数へのポインタに代入と呼び出しfnc = (int (*)(void *, void *)) cmp;(*fnc)(&x, &y);
void 型と void 型へのポインタ• void(= 空洞 ) つまり大きさがない
• 関数に引数や戻り値がないことを意味する• ポインタの指し示す先の大きさが不明 ( 特定
の型に縛られない ) であることを意味する
20
変数の宣言void a; // void 型の変数はないのでコンパイルエラー
void *p; // p が void 型へのポインタ
大きさが 0 バイトのデータつまり void a; には意味がないが大きさが不明のデータでも先頭アドレスつまり void *p; には意味がある
0x ~ 0
0x ~ 1
0x ~ 2
0x ~ 3
0x ~ 0
0x ~ 1
0x ~ 2
0x ~ 3
p=0x ~ 0
void 型と void 型へのポインタ• void * 型は使用時に大きさを決めて使
う• 適当な型へのポインタとしてキャストする
21
void 型ポインタの例int a;
void *p = &a; // p に a のアドレスが入る*((int *)p) = 1; // p は void* なので *p は void だが // p を int* にキャストすると *p が int になる
0x ~ 0
0x ~ 1
0x ~ 2
0x ~ 3
0x ~ 0
0x ~ 1
0x ~ 2
0x ~ 3
*p は void なので意味がない*((int*)p) は int なので意味がある
p=0x ~ 0
整数除算の商と剰余• 知っているようで知らない商と剰余の定義• 整数についてとした時、が商、 が剰余• 剰余の一般形:
例: 5 / 3 = 1 余り 2、 2 余り -1-5 / 3 = -1 余り -2、 -2 余り 1 5 / -3 = -1 余り 2、 -2 余り 1-5 / -3 = 1 余り -2、 2 余り 1
• 最小非負剰余: 例: 5 / 3 = 1 余り 2
-5 / 3 = -2 余り 1 5 / -3 = -1 余り 2-5 / -3 = 2 余り 1
• 絶対値最小剰余: 例: 5 / 3 = 2 余り -1-5 / 3 = -2 余り 1 5 / -3 = -2 余り 1-5 / -3 = 2 余り 1
23
追加の制約がないと一意に決まらない
一意に決まるがの符号によりの絶対値が変わる
一意に決まるがが共に正の場合感覚に合わない
負の数の除算と剰余算• C89では実装依存だった
• 「/に対する切捨ての方向および%に対する結果の符号は,負の被演算子に対しては機種依存する」([1]p.50)
• 例: 以下のいずれも有り得る• 5 / 3 = 1 余り 2• -5 / 3 = -1 余り -2、-2 余り 1• 5 / -3 = -1 余り 2、-2 余り -1• -5 / -3 = 1 余り -2、 2 余り 1
• C99以降は以下のように定義された• a/bはゼロに向かった切捨て• (a/b)*b + a%b は a と等しい• 例: 必ず以下のようになる
• 5 / 3 = 1 余り 2• -5 / 3 = -1 余り -2• 5 / -3 = -1 余り 2• -5 / -3 = 1 余り -2
24
きちんと定義されたので安心して使えるようになったa,b が共に正の時と商と剰余の絶対値も一致して使い易い
実装依存なので安心して使えない
絶対値最小の商
除算と剰余算 : C89
K&R 第 2版 p.50
整数の割り算では小数部分は切り捨てられる。式x % yは x を y で割った余りで、 x がyでちょうど割り切れるなら 0 となる。
/ に対する切捨ての方向および % に対する結果の符号は、負の被演算子に対しては機種依存する。
25
除算と剰余算 : C99ISO/IEC 9899:1999 Programming languages C (C99) + TC1 + TC2 + TC3Committee Draft - September 7, 2007 p.82.
6.5.5 Multipliative operators5 The result of the / operator is the quotient from the division of the
first operand by the second; the result of the % operator is the remainder. In both operations, if the value of the second operand is zero, the behavior is undefined.
6 When integers are divided, the result of the / operator is the algebraic quotient with any fractional part discarded.90) If the quotient a/b is representable, the expression (a/b)*b + a%b shall equal a.90) This is often call "truncated toward zero".
6.5.5 乗算演算子7 / 演算子の結果は1つ目のオペランド(演算対象)を2つ目のオペランドで除算した商であり; %
演算子の結果は剰余である。両方の演算子は、もし2つ目のオペランドがゼロなら、動作は未定義である。
8 整数が除算される場合、/ 演算子の結果は小数部が破棄された代数的な商になる。 90) もし商a/bが表現可能なら、式 (a/b)*b + a%b は a と等しくなくてはならない。90) これはしばしば「ゼロに向かった切捨て」と呼ぶ。
26
除算と剰余算 : C11ISO/IEC 9899:2011 Programming languages C (C11)Committee Draft - April 12, 2011 p.92.
6.5.5 Multipliative operators6 When integers are divided, the result of the / operator is
the algebraic quotient with any fractional part discarded.105) If the quotient a/b is representable, the expression (a/b)*b + a%b shall equal a ; otherwise, the behavior of both a/b and a%b is undefined.105) This is often call "truncated toward zero".
6.5.5 乗算演算子7 整数が除算される場合、/ 演算子の結果は小数部が破棄された代数的な商にな
る。 105) もし商 a/b が表現可能なら、式 (a/b)*b + a%b は a と等しくなくてはならない; そうでないなら、a/b および a%b の両方の動作は未定義である。105) これはしばしば「ゼロに向かった切捨て」と呼ぶ。
27
C 言語の仕様書OpenStandardsISO/IEC JTC1/SC22 - Programming languages and, operating systemsWG14 - Chttp://www.open-std.org/JTC1/SC22/WG14/
WG14 N1256ISO/IEC 9899:1999 Programming languages C (C99) + TC1 + TC2 + TC3Committee Draft - September 7, 2007http://www.open-std.org/JTC1/SC22/WG14/www/docs/n1256.pdf
WG14 N1570ISO/IEC 9899:2011 Programming languages C (C11)Committee Draft - April 12, 2011http://www.open-std.org/JTC1/SC22/WG14/www/docs/n1570.pdf
28
注 : Committee Draft (委員会草案 ) なので最終版ではない正式版の仕様書は有料 : ISO/IEC 9899:2011
ここに書いてあることがC 言語のすべて書いてないことは実装依存
ユークリッドの互除法Euclidean algorithm
• 最大公約数 (GCD: Greatest Common Divisor)を求めるアルゴリズム
29
Wikipedia / ユークリッドの互除法
ユークリッドの互除法Euclidean algorithm
• 整数についてをで割った商を余りををとし以下のように定義する
• を適当な整数とすれば• をの任意の公約数とすると、と書けるから
をの任意の公約数とすると、と書けるから
• つまり• の公約数は必ずの約数となり• の公約数は必ずの約数となる
• 従っての公約数との公約数は等しい集合である• よってとの最大公約数は等しい
• このことからの最大公約数を求めればの最大公約数が求まる
30
ユークリッドの互除法Euclidean algorithm
• ここでの余りはであるから、以下の手順を行うとに収束する1. の余りを求める2. を新たなとする3. がで割り切れるまで手順 1,2 を繰り返す
• つまり上記手順 1. でになった時のが最初に与えたの最大公約数である
31
n = 0 の時 m % n が0 割りになるので具合が悪い
ユークリッドの互除法Euclidean algorithm
• ここでの余りはであるから、以下の手順を行うとに収束する1. が 0 になるまで以下の手順 2,3 を繰り返す2. の余りを求める3. を新たなとする
• つまり上記手順 1. でになった時のが最初に与えたの最大公約数である
32
n = 0 の時 m % n が0 割りにならないように工夫
ユークリッドの互除法Euclidean algorithm
• の余りを求める• r = m % n;
• を新たなとする• m = n;• n = r;
• がで割り切れるまで繰り返す• r != 0 の間ループさせる• 無限ループで r == 0 の時 break や return させる
• n < 0 ? -n : n;• abs(n);• n * sign(n);
33
n = 0 の時 m % n すると0 割りになるので具合が悪いため厳密には前判定ループで n == 0 でループを辞める方が都合が良い
ユークリッドの互除法Euclidean algorithm
• 実装は色々出来る
34
gcd.cfor (;;) {
r = m % n; if (r == 0) return n < 0 ? -n : n; m = n; n = r;}
gcd.cdo {
r = m % n; m = n; n = r;} while (r);return m < 0 ? -m : m;
gcd.cr = 1;
while (r) { r = m % n; m = n; n = r;};return m < 0 ? -m : m;
gcd.cwhile (1) {
r = m % n; if (r == 0) break; m = n; n = r;};return n < 0 ? -n : n;
gcd.cwhile (r = m % n) {
m = n; n = r;};return n < 0 ? -n : n;
若干まずいやり方n = 0 だと m % n が0 割りで不具合を生じる
ユークリッドの互除法Euclidean algorithm
• 実装は色々出来る
35
gcd.cwhile (n) {
r = m % n; m = n; n = r;};return m < 0 ? -m : m;
C99未満の仕様だとm,n が共に正でない場合おかしな結果が出るかもしれない点に注意
演習 :m = 0 のときはどうなるだろう?何らかの例外処理が必要でないか検討しなさい
演習 : gcd.c
• 整数 m,n の最大公約数を計算する関数 gcd を作成せよ
• gcd_test.c と共にコンパイルする事で動作を確認せよ
• 引数• int m,n : 任意の整数
• 戻り値• m,n の最大公約数を int 型で返す
36
mintty + bash + GNU C$ gcc gcd_test.c gcd.c && ./a
m = ? 48n = ? 15gcd(48, 15) = 3