プログラミング論 ポインタ http://www.ns.kogakuin.ac.jp/~ct13140/Prog/ 1 見つかった誤り if( x = 3 ){ ... } "x=3"は比較ではなく、代入. x += 2 は、"xが2増える"が, x =+ 2 は、"x に +2 を代入"となる. つまり "x = +2"となってしまう N-2 見つかった誤り x = x+1; はOK. x+1 = x; はNG. 代入は 右の値を左に入れる. x = 3 はOKだが, 3 = x はNG. N-3 見つかった誤り • #define MAX 100 は,MAXを100に置き換えてからコンパイルする – MAXという変数があるわけではない! #define MAX 100 i=MAX; #define MAX 100 MAX=30; MAXという変数が あるわけではない これを i=100; コンパイル これを 100=30; コンパイル???? N-4 概要 • ポインタ – 難易度が高いが,極めて重要 – & と * の2種類しかない. • ちなみに… –前期最重要事項 : 関数 –後期最重要事項 : pointer N-5 5 ポインタ • なにかを「指す」ものがポインタである. • C言語ではアドレスを理解すれば良い. • 一般に理解が難しいとされる N-6 メモリとアドレス • 計算機はメモリにデータを保存している. – 通常1バイト(8bit)単位で管理される. – 1バイトの「記憶領域=入れ物」が大量にある. • 各バイトには,固有の通し番号(住所,アドレス) がついている. それぞれのメモリに 1バイトのデータを 記憶できる. 各1バイトに固有の 住所がついている. 0番地 1番地 2番地 3番地 4番地 5番地 6番地 7番地 8番地 9番地 7 100 56 23 53 45 95 1バイトの記憶領域=入れ物が10個ある例. 2 0 65 N-7 変数の記憶領域 • 変数を作ると,当然メモリ上にそのための記憶領 域が自動的に確保される. char abc; /* char型変数を1個用意 */ 変数abcの内容は, 4番地に記録する ことに決めたとする. (計算機が自動で行う) abc 0番地 1番地 2番地 3番地 4番地 5番地 6番地 7番地 8番地 9番地 7 100 56 23 53 45 95 2 0 65 N-8 変数の記憶領域 • 変数を作ると,当然メモリ上にそのための記憶領 域が自動的に確保される. char abc; /* char型変数を1個用意 */ char *p; &abc は,「変数abcは メモリの何番地か」を返す. abc = 7; pには「4番地」が入る. p = &abc; 7では無いことに注意! abc 0番地 1番地 2番地 3番地 4番地 5番地 6番地 7番地 8番地 9番地 7 100 56 23 7 45 95 2 0 65 N-9 &演算子 と *演算子 (重要!) 変数は,メモリのどこかに対応している • &演算子は「変数のアドレス」を取得する演算子 – 使い方:変数名の前に&記号をつける – 意味:その変数の位置、アドレス – 例 p=&abc; /*変数pにアドレスを代入*/ • *演算子は「メモリの中身」を取得する演算子 – 使い方:アドレス(アドレス変数)の前に*記号を付ける – 意味:「そのアドレスのメモリの中身」の中身 – 例 x=*p; /*pにはアドレスが格納されているとする*/ N-10 &演算子 • &演算子は変数のアドレスを調べる演算子. • 変数の前に&を付けると,その変数アドレスが得 られる. void main(){ char abc; char *p; p = &abc; printf("abcのアドレスは%pです.\n", p); 実行結果 } abcのアドレスは0013FF7Cです. N-11 &演算子 void main(){ char abc, def; char *p; p = &abc; printf("abcのアドレスは%pです.\n", p); p = &def; printf("defのアドレスは%pです.\n", p); } 実行結果 abcのアドレスは0013FF7Cです. defのアドレスは0013FF78です. N-12 &演算子 void main(){ char abc, def, ghi; printf("アドレスは%p %p %pです.\n", &abc, &def, &ghi); } 実行結果 アドレスは0013FF7C 0013FF78 0013FF74です. N-13 &演算子 void main(){ char abc[5]; printf("アドレスは%p %p\n%p %p %pです.\n", &(abc[0]), &(abc[1]), &(abc[2]), &(abc[3]), &(abc[4])); } 実行結果 アドレスは0013FF78 0013FF79 0013FF7A 0013FF7B 0013FF7Cです. N-14 &演算子 void main(){ int abc[5]; printf("アドレスは%p %p\n%p %p %pです.\n", &(abc[0]), &(abc[1]), &(abc[2]), &(abc[3]), &(abc[4])); } int型は4バイト 実行結果 アドレスは0013FF6C 0013FF70 0013FF74 0013FF78 0013FF7Cです. N-15 アドレス型変数(ポインタ変数) • アドレスを格納する変数 – 整数を格納できる変数があるように, アドレスを格納する変数がある. char ch; /* char型を格納する */ char *p; /* char型変数のアドレスを格納 */ p = &ch; /* ch は"4番地"なので,pには"4番地"が入る ch 0番地 1番地 2番地 3番地 4番地 5番地 6番地 7番地 8番地 9番地 7 100 56 23 7 45 95 2 0 65 N-16 アドレス型変数(ポインタ変数) • アドレスを格納する変数 – 整数を格納できる変数があるように, アドレスを格納する変数がある. char ch; /* char型を格納する */ char *p; /* char型変数のアドレスを格納 */ p = &ch; printf("アドレスは%pです\n", p); ポインタ(アドレス型)をprintfで表示するには, %p を使う. N-17 *演算子 変数a,b,pを宣言した. それぞれ,メモリの 6番地,5番地,4番地が 割与えられたとする. • アドレスの間接参照演算子. • アドレスの中身にアクセスする. a,bは「整数(char型の char a, b; char *p; 値)」を入れる入れ物. pは「アドレス」を入れる 入れ物. 注意:実際はアドレスは1 バイトではない. p b a 0番地 1番地 2番地 3番地 4番地 5番地 6番地 7番地 8番地 9番地 N-18 *演算子 • アドレスの間接参照演算子. • アドレスの中身にアクセスする. char a, b; char *p; 変数aに53を代入. a = 53; つまり,メモリの6番地に 53を格納. p b a 0番地 1番地 2番地 3番地 4番地 5番地 6番地 7番地 8番地 9番地 53 N-19 *演算子 &aは「アドレス」. pは「アドレスを入れ る入れ物」. p=&aは正しい. • アドレスの間接参照演算子. • アドレスの中身にアクセスする. char a, b; char *p; &aで変数aのアドレスを得る. 変数aは6番地なので, a = 53; pには6番地が代入される. p = &a; 注意:実際は「番地」という 単位はつかない. p b a 0番地 1番地 2番地 3番地 4番地 5番地 6番地 7番地 8番地 9番地 6 番地 53 N-20 *演算子 • アドレスの間接参照演算子. アドレス(ポインタ)に • アドレスの中身にアクセスする. * を付けると, そのアドレスの中身を char a, b; char *p; 意味する. a = 53; pは「6番地」. p = &a; *pは「6番地の中身」 すなわち53 b = *p; p b a 0番地 1番地 2番地 3番地 4番地 5番地 6番地 7番地 8番地 9番地 6 53 番地 53 N-21 &演算子 と *演算子 (1) char a; char *p; aが5番地に, pが4番地に 割り当てられたとする. aは「整数(char)」を入れる入れ物. pは「アドレス」を入れる入れ物. p a 0番地 1番地 2番地 3番地 4番地 5番地 6番地 7番地 8番地 9番地 N-22 &演算子 と *演算子 (1) char a; char *p; p = &a; p &a で, aのアドレスを獲得. &a は"5番地"である. pに"5番地"が 格納される. (メモリの4番地に "5番地"が格納される) a 0番地 1番地 2番地 3番地 4番地 5番地 6番地 7番地 8番地 9番地 5 番地 N-23 &演算子 と *演算子 (1) char a; char *p; p = &a; a = 8; p aに8を代入. aは5番地なので, メモリの5番地に 8が格納される. a 0番地 1番地 2番地 3番地 4番地 5番地 6番地 7番地 8番地 9番地 5 番地 8 N-24 &演算子 と *演算子 (1) char a; char *p; p = &a; a = 8; printf("%d", *p); pは5番地. *pは「5番地の中身」. 5番地には, "8"が格納されているので, 8が表示される. 実行結果 8 実質,a と *p は同一. p a 0番地 1番地 2番地 3番地 4番地 5番地 6番地 7番地 8番地 9番地 5 番地 8 N-25 &演算子 と *演算子 (2) char a; char *p; aが5番地に, pが4番地に 割り当てられたとする. p a 0番地 1番地 2番地 3番地 4番地 5番地 6番地 7番地 8番地 9番地 N-26 &演算子 と *演算子 (2) char a; char *p; p = &a; p &a で, aのアドレスを獲得. &a は"5番地"である. pに"5番地"が 格納される. (メモリの4番地に "5番地"が格納される) a 0番地 1番地 2番地 3番地 4番地 5番地 6番地 7番地 8番地 9番地 5 番地 N-27 &演算子 と *演算子 (2) char a; char *p; p = &a; *p = 8; pは5番地. *pは「5番地の中身」. 「5番地の中身」を "8"にする. 結果,aが"8"になる. p a 0番地 1番地 2番地 3番地 4番地 5番地 6番地 7番地 8番地 9番地 5 番地 8 N-28 &演算子 と *演算子 (2) aを表示する. 当然,"8"が 表示される. char a; char *p; p = &a; *p = 8; printf("%d", a); 実質,「aへの代入」と 「*pへの代入」は 同一である. 実行結果 8 p a aへの値の代入は 行っていないが, aの値が変わる. 0番地 1番地 2番地 3番地 4番地 5番地 6番地 7番地 8番地 9番地 5 番地 8 N-29 &演算子 と *演算子 (3) char a; char *p; p = &a; a = 7; p aに"7"を代入. すなわち, メモリの5番地に "7"を格納. a 0番地 1番地 2番地 3番地 4番地 5番地 6番地 7番地 8番地 9番地 5 番地 7 N-30 &演算子 と *演算子 (3) char a; char *p; p = &a; a = 7; *p = 8; p pは「5番地」 *p=8 は, 「メモリ5番地を8にする」 aの値が変わってしまった. a 0番地 1番地 2番地 3番地 4番地 5番地 6番地 7番地 8番地 9番地 5 番地 8 N-31 &演算子 と *演算子 (3) char a; char *p; p = &a; a = 7; *p = 8; printf("%d", a); aの値を表示する. 当然,"8"が表示される. 実行結果 8 p a 0番地 1番地 2番地 3番地 4番地 5番地 6番地 7番地 8番地 9番地 5 番地 8 N-32 アドレス型変数(ポインタ変数) • アドレスを格納する変数 – 整数を格納できる変数があるように, アドレスを格納する変数がある. char int char int ch; i; *p0; *p1; /* /* /* /* 整数や文字コードの入れ物 */ 整数の入れ物 */ char型変数のアドレスの入れ物 */ int型変数のアドレスの入れ物 */ N-33 アドレス型変数(ポインタ変数) char a; /* a は char型 */ char *b; /* b は アドレス型 */ char c, *d, e, *f; /* cは char型,d はアドレス型, eは char型,f はアドレス型 */ char *g, h; /* g はアドレス型, hはchar型 */ 不慣れなうちは,この様な記述は避けるのが無難? N-34 アドレス型変数(ポインタ変数) • 変数の宣言 と 変数の使用 int i; 「int型」の変数iを作成. i = 3; 作成済み変数iに値を代入. int *p; p = &i; *p = 4; 「int*」型の変数pを作成. 作成済み変数pに値を代入. *pというメモリに4を代入. N-35 void main(){ 練習0 int i, *j, k; i = 3; j = &i; k = i; *j = 4; k = 5; printf("i=%d\n", i); } N-36 配列とアドレス(ポインタ) • 配列の場合,アドレスは連続している. – char ch[10]が100番地~109番地に割り振られ た例. ch[0] ch[1] ch[2] ch[3] ch[4] ch[5] ch[6] ch[7] ch[8] ch[9] 100 番地 101 番地 102 番地 103 番地 104 番地 105 番地 106 番地 107 番地 108 番地 109 番地 N-37 配列とアドレス(ポインタ) void main(){ char abc[5]; printf("アドレスは%p %p\n%p %p %pです.\n", &(abc[0]), &(abc[1]), &(abc[2]), &(abc[3]), &(abc[4])); } 実行結果 アドレスは0013FF78 0013FF79 0013FF7A 0013FF7B 0013FF7Cです. N-38 配列とアドレス(ポインタ) void main(){ int abc[5]; printf("アドレスは%p %p\n%p %p %pです.\n", &(abc[0]), &(abc[1]), &(abc[2]), &(abc[3]), &(abc[4])); } 実行結果 アドレスは0013FF6C 0013FF70 0013FF74 0013FF78 0013FF7Cです. N-39 配列とアドレス(ポインタ) • 配列の場合,アドレスは連続している. – int &( &( &( i[3]が100番地~111番地に割り振られた例 i[0] ) は,100番地 (この例では) int型は i[1] ) は,104番地 4バイトである. i[2] ) は,108番地 i[0] 100 番地 101 番地 102 番地 i[1] 103 番地 104 番地 105 番地 106 番地 i[2] 107 番地 108 番地 109 番地 110 番地 111 番地 N-40 配列とアドレス(ポインタ) char ch[12]; char *p; p = &(ch[0]); printf("%p\n", p); p = ch; printf("%p\n", p); 実行結果 0013FF74 0013FF74 添え字を付けずに ([0]などを付けずに) 配列名だけを書くと, 配列の先頭のアドレス を意味する. この場合,ch と &(ch[0]) は同義. N-41 ポインタ演算 char ch[10]; char *p; p = ch; printf("%p\n", p); p++; printf("%p\n", p); 実行結果 0013FF74 0013FF75 N-42 配列とアドレス(ポインタ) char ch[10]; char *p; p = ch; pには, ch[10]の先頭アドレスである 100番地が代入される. ch[10]が100番地~109番地に割り当てられたとする. ch[0] ch[1] ch[2] ch[3] ch[4] ch[5] ch[6] ch[7] ch[8] ch[9] 100 番地 101 番地 102 番地 103 番地 104 番地 105 番地 106 番地 107 番地 108 番地 109 番地 N-43 配列とアドレス(ポインタ) char ch[10]; char *p; p = ch; pが100番地なので, 「100番地の内容」を *p = 1; "1"にする. つまり,ch[0]が "1"になる. ch[0] ch[1] ch[2] ch[3] ch[4] ch[5] ch[6] ch[7] ch[8] ch[9] 100 番地 101 番地 102 番地 103 番地 104 番地 105 番地 106 番地 107 番地 108 番地 109 番地 1 N-44 配列とアドレス(ポインタ) char ch[10]; char *p; p = ch; pの値は「100番地」だった. *p = 1; pが1増えて, 「101番地」になる. p++; ch[0] ch[1] ch[2] ch[3] ch[4] ch[5] ch[6] ch[7] ch[8] ch[9] 100 番地 101 番地 102 番地 103 番地 104 番地 105 番地 106 番地 107 番地 108 番地 109 番地 1 N-45 配列とアドレス(ポインタ) char ch[10]; char *p; p = ch; pの値は「101番地」. 101番地の中身が *p = 1; "2"になる. すなわちch[1]が p++; "2"になる. *p = 2; ch[0] ch[1] ch[2] ch[3] ch[4] ch[5] ch[6] ch[7] ch[8] ch[9] 100 番地 101 番地 1 2 102 番地 103 番地 104 番地 105 番地 106 番地 107 番地 108 番地 109 番地 N-46 配列とアドレス(ポインタ) char ch[10]; char *p; p = ch; pはch[0]を指している. *p = 1; p++; pはch[1]を指している. *p = 2; ch[0] ch[1] ch[2] ch[3] ch[4] ch[5] ch[6] ch[7] ch[8] ch[9] 100 番地 101 番地 1 2 102 番地 103 番地 104 番地 105 番地 106 番地 107 番地 108 番地 109 番地 N-47 ポインタ演算 int i[10]; int *p; p = i; printf("%p\n", p); p++; printf("%p\n", p); i[0] 0013 FF74 番地 0013 FF75 番地 0013 FF76 番地 実行結果 0013FF74 0013FF78 差が4 i[1] 0013 FF77 番地 0013 FF78 番地 0013 FF79 番地 0013 FF7a 番地 i[2] 0013 FF7b 番地 0013 FF7c 番地 0013 FF7d 番地 0013 FF7e 番地 0013 FF7f 番地 N-48 ポインタ演算 • int型変数のアドレスに1足すと, 4バイト分増加して,次のintのアドレスになる. – 減算も同様. • char型変数のアドレスを1足すと, 1バイト分増加して,次のcharのアドレスになる. – ただし, int型が4バイトとは限らない. 処理系による.ほとんどの例で4バイト. char型は必ず1バイト. N-49 ポインタ演算 p++を行うと, char *p; の場合,アドレスが1番地進む. int *p; の場合,アドレスが4番地進む. double *p の場合,アドレスが8番地進む. (ただし,int型が4バイト,double型が8バイトの 場合) N-50 ポインタ演算 • 以下の様な操作が可能. – アドレスに整数を加算/減算して,次の/前のアドレス に格納されているデータにアクセス. – アドレス同士の引き算をし,何個離れているか調べる • 以下の様な操作は行えない. – アドレスに乗算/除算を行う. – アドレス同士の加算. N-51 ポインタ演算 (OK) char *p, *q; int i; の場合, p++; p--; p=q+2; p=q-2; OK. OK. OK. OK. pのアドレスが1番地増える. pのアドレスが1番地減る. pはqの2番地後. pはqの2番地前. i=p-q; OK. iにはpとqの差が代入される. N-52 ポインタ演算 (NG) char *p, *q, *r; の場合, p=q+r; p=q*2; p=q/2; p=q+1.2; NG.アドレス同士の加算はNG. NG.アドレスに乗算と除算はNG. NG.アドレスに乗算と除算はNG. NG.加算減算は整数のみ. N-53 *演算子 と [] int *p; int x[10]; p = x; のとき p と &(x[0]) と x は同義. p+1 と &(x[1]) と x+1 は同義. p+2 と &(x[2]) と x+2 は同義. N-54 *演算子 と [] int *p; int x[10]; p = x; のとき *p と x[0] と *x は同義. *(p+1) と x[1] と *(x+1) は同義. *(p+2) と x[2] と *(x+2) は同義. N-55 練習1 void main(){ int i[2], *j; i[0]=3; i[1]=7; j=i; j++; printf("%d\n", *j); } N-56 練習2 void main(){ int i[2], *j; i[0]=3; i[1]=7; j=i; (*j)++; printf("%d\n", *j); } N-57 注意 int *p, x[10]; p = x; のとき p++; x++; ←これはOK. ←これはNG. x という変数があるわけではない. x は &(x[0]) という定数. よって,読めるが書けない. N-58 関数の仮引数と実引数(値渡し) void func(int a){ a = 7; } x void main(){ int x = 3; func(x); printf("%d\n", x); } 100 番地 3 N-59 関数の仮引数と実引数(値渡し) void func(int a){ a = 7; } x void main(){ int x = 3; func(x); printf("%d\n", x); } 100 番地 3 N-60 関数の仮引数と実引数(値渡し) void func(int a){ a = 7; } void main(){ int x = 3; func(x); printf("%d\n", x); } x a 100 番地 104 番地 3 3 別のメモリに割り当てられた 別の変数 N-61 関数の仮引数と実引数(値渡し) void func(int a){ a = 7; } void main(){ int x = 3; func(x); printf("%d\n", x); } x a 100 番地 104 番地 3 7 別のメモリに割り当てられた 別の変数 N-62 関数の仮引数と実引数(値渡し) void func(int a){ a = 7; } x void main(){ int x = 3; func(x); printf("%d\n", x); } 実行結果 3 100 番地 3 N-63 関数の仮引数と実引数(値渡し) void func(int x){ x = 7; } void main(){ int x = 3; func(x); printf("%d\n", x); } 実行結果 3 この2個を 同じ名前にしても 結果は変わらない. x x 100 番地 104 番地 3 3 別のメモリに割り当てられた 別の変数 N-64 関数の仮引数と実引数(参照渡し) void func(int *p){ *p = 7; } void main(){ int x = 3; int *px; px = &x; func(px); printf("%d\n", x); } x px 100 番地 104 番地 3 N-65 関数の仮引数と実引数(参照渡し) void func(int *p){ *p = 7; } void main(){ int x = 3; int *px; px = &x; func(px); printf("%d\n", x); } x px 100 番地 104 番地 3 100 番地 N-66 関数の仮引数と実引数(参照渡し) void func(int *p){ *p = 7; } void main(){ int x = 3; int *px; px = &x; func(px); printf("%d\n", x); } x px 100 番地 104 番地 3 100 番地 N-67 関数の仮引数と実引数(参照渡し) void func(int *p){ *p = 7; } void main(){ int x = 3; int *px; px = &x; func(px); printf("%d\n", x); } x px p 100 番地 104 番地 108 番地 3 100 100 番地 番地 N-68 関数の仮引数と実引数(参照渡し) void func(int *p){ *p = 7; } void main(){ int x = 3; int *px; px = &x; func(px); printf("%d\n", x); } x px p 100 番地 104 番地 108 番地 7 100 100 番地 番地 N-69 関数の仮引数と実引数(参照渡し) void func(int *p){ *p = 7; } void main(){ int x = 3; int *px; px = &x; func(px); printf("%d\n", x); } 実行結果 7 x px 100 番地 104 番地 7 100 番地 N-70 応用例:値を2個返す関数 C言語では,戻り値は1個しか用意できない. void sq_cu(int n, int *ps, int *pc){ *ps = n*n; *pc = n*n*n; squareが100番地, } cubeが104番地で void main(){ あったとする. int square, cube; sq_cu(3, &square, &cube); printf("%d %d\n", square, cube); } N-71 応用例:値を2個返す関数 C言語では,戻り値は1個しか用意できない. void sq_cu(int n, int *ps, int *pc){ *ps = n*n; *pc = n*n*n; 引数を 3,100番地,104番地 } として,関数にジャンプ. void main(){ int square, cube; sq_cu(3, &square, &cube); printf("%d %d\n", square, cube); } squareが100番地,cubeが104番地であったとする. N-72 応用例:値を2個返す関数 C言語では,戻り値は1個しか用意できない. void sq_cu(int n, int *ps, int *pc){ *ps = n*n; nが3, *pc = n*n*n; psが100番地, pcが104番地として } 関数sq_cuを開始. void main(){ int square, cube; sq_cu(3, &square, &cube); printf("%d %d\n", square, cube); } squareが100番地,cubeが104番地であったとする. N-73 応用例:値を2個返す関数 C言語では,戻り値は1個しか用意できない. void sq_cu(int n, int *ps, int *pc){ *ps = n*n; psは"100番地". 「100番地の中身を *pc = n*n*n; 3*3にする」 } という処理. これにより void main(){ squareの値が int square, cube; 変わる. sq_cu(3, &square, &cube); printf("%d %d\n", square, cube); } squareが100番地,cubeが104番地であったとする. N-74 応用例:値を2個返す関数 C言語では,戻り値は1個しか用意できない. void sq_cu(int n, int *ps, int *pc){ *ps = n*n; pcは"104番地". 「104番地の中身を *pc = n*n*n; 3*3*3にする」 } という処理. これにより void main(){ cubeの値が int square, cube; 変わる. sq_cu(3, &square, &cube); printf("%d %d\n", square, cube); } squareが100番地,cubeが104番地であったとする. N-75 応用例:値を2個返す関数 C言語では,戻り値は1個しか用意できない. void sq_cu(int n, int *ps, int *pc){ *ps = n*n; 実行結果 *pc = n*n*n; 9 27 } void main(){ int square, cube; sq_cu(3, &square, &cube); printf("%d %d\n", square, cube); } squareが100番地,cubeが104番地であったとする. N-76 やってはいけないこと void main(){ int i=0, j[2]; j[0] = 7; j[1] = 8; /* OK! */ j[2] = 9; /* NG! */ printf("%d\n", i); } 実行結果 9 j[0] 84 番地 85 番地 86 番地 7 j[1] 87 番地 88 番地 89 番地 90 番地 8 i 91 番地 92 番地 93 番地 94 番地 95 番地 9 N-77 やってはいけないこと void main(){ int i[2], *p; p = i; /* OK. pはi[0]のアドレス */ *p = 7; /* OK. i[0] が 7 になる */ p++; /* OK. pはi[1]のアドレス */ *p = 8; /* OK. i[1] が 8 になる */ p++; /* NG.pはi[2]のアドレス? */ *p = 9; /* NG! i[2]相当の場所に 書き込みをしてしまう! */ } N-78 難しい! ポインタへのポインタ int i; int *p; int **pp; iは int(整数)を入れる箱. pは 「intの箱のアドレス」を入れる箱. ppは「『アドレスを入れる箱』のアドレス」を入れる箱. i 84 番地 85 番地 p 86 番地 87 番地 88 番地 89 番地 pp 90 番地 91 番地 92 番地 93 番地 94 番地 95 番地 N-79 難しい! ポインタへのポインタ int i; i = 3; int *p; int **pp; i 84 番地 85 番地 p 86 番地 87 番地 88 番地 89 番地 pp 90 番地 91 番地 92 番地 93 番地 94 番地 95 番地 3 N-80 難しい! ポインタへのポインタ int i; int *p; i = 3; p = &i; int **pp; i 84 番地 85 番地 p 86 番地 3 87 番地 88 番地 89 番地 pp 90 番地 91 番地 92 番地 93 番地 94 番地 95 番地 84番地 N-81 難しい! ポインタへのポインタ int i; int *p; i = 3; p = &i; pp = &p; int **pp; i 84 番地 85 番地 p 86 番地 3 87 番地 88 番地 89 番地 pp 90 番地 84番地 91 番地 92 番地 93 番地 94 番地 95 番地 88番地 N-82 難しい! ポインタへのポインタ int i; int *p; int **pp; i = 3; 実行結果 p = &i; 3 pp = &p; printf("%d\n", **pp); i 84 番地 85 番地 p 86 番地 3 87 番地 88 番地 89 番地 pp 90 番地 84番地 91 番地 92 番地 93 番地 94 番地 95 番地 88番地 N-83 難しい! ポインタへのポインタ int i, *p, **pp; i = 3; p = &i; pp = &p; printf("%d\n", **pp); i 84 番地 85 番地 *(*pp) は *(84番地) なので, 「84番地の中身」となり "3"を意味する. p 86 番地 3 *ppは「88番地の中身」 なので, 「84番地」を意味する. 87 番地 88 番地 89 番地 pp 90 番地 84番地 91 番地 92 番地 93 番地 94 番地 95 番地 88番地 N-84 多次元配列の配置 • int a[3][2]は, 「長さ2の配列」が3本であって, 「長さ3の配列」が2本ではない? int a[3][2]は以下の順で割り当てられる. a[0][0] a[0][1] a[1][0] a[1][1] a[2][0] a[2][1] N-85 復習 i void main(){ int i=10, j=11; 104番地 int *p; の中身 p=&j; printf("*p=%d\n",*p); *p=7; printf("i=%d j=%d",i,j); } 104番地 の中身 j p 100 104 108 番地 番地 番地 10 11 10 11 10 11 104 10 11 104 10 7 104 10 7 104 *p=11 実行結果 i=10 j=7 N-86 練習 3 void main(){ int x=10, y=11, z=12; int *p; printf("x=%d y=%d z=%d\n",x,y,z); p=&x; *p=5; printf("x=%d y=%d z=%d\n",x,y,z); p=&y; *p=6; printf("x=%d y=%d z=%d\n",x,y,z); p=&z; *p=7; printf("x=%d y=%d z=%d\n",x,y,z); } 何と表示される? N-87 練習 4 void main(){ 何と表示される? int x[3]; int *p; x[0]=10; x[1]=20; x[2]=30; printf("x[0]=%d x[1]=%d x[2]=%d\n",x[0],x[1],x[2]); p=x; /* p=&(x[0]) の意味 */ *p=7; printf("x[0]=%d x[1]=%d x[2]=%d\n",x[0],x[1],x[2]); p++; *p=6; printf("x[0]=%d x[1]=%d x[2]=%d\n",x[0],x[1],x[2]); p++; *p=5; printf("x[0]=%d x[1]=%d x[2]=%d\n",x[0],x[1],x[2]); } N-89 練習 5 void main(){ 何と表示される? int x[3]; int *p; x[0]=10; x[1]=20; x[2]=30; printf("x[0]=%d x[1]=%d x[2]=%d\n",x[0],x[1],x[2]); p=x; /* p=&(x[0]) の意味 */ *p=7; printf("x[0]=%d x[1]=%d x[2]=%d\n",x[0],x[1],x[2]); (*p)++; printf("x[0]=%d x[1]=%d x[2]=%d\n",x[0],x[1],x[2]); *p=6; printf("x[0]=%d x[1]=%d x[2]=%d\n",x[0],x[1],x[2]); } N-91
© Copyright 2025 ExpyDoc