第6章 ポインタ ポインタが分からずにC言語を投げ出す人が数多くいます。 その半面、使いこなせば強力な武器となります。 しっかりと学習していきましょう 第6章 ポインタ 1 変数とメモリ int a; a = 6; printf("%d“,a); という文が実行された時、どうなるでしょうか? 宣言するとコンピュータのメモリと呼ばれる 記憶領域内に4Byte分の領域が確保される 代入すれば、その領域に値が保存される 参照すると、その領域からデータが取得される 宣言 メモリ 代入 メモリ 6 場所確保 参照 メモリ 6 6を保存! 第6章 ポインタ 6が保存されてる から6を表示 2 アドレス(1) 変数を宣言すると場所が確保される その確保された場所を表す住所のようなものをアドレスという メモリ 6 ここの住所は 008F3D29番地です 変数のアドレスの取得はアドレス演算子で取得できる 演算子 意味 アドレス取得 & 優先順位 3 種類 単項 また、アドレスの表示の際はフォーマット指定子%pを用いる 第6章 ポインタ 3 アドレス(2) 次のプログラムを実行しよう #include <stdio.h> int main(void){ int a; double b; printf("aのアドレスは%p,bのアドレスは%pです\n”, &a, &b); return 0; } 実行結果は毎回異なります 第6章 ポインタ 4 ポインタ変数(1) アドレスを格納する変数のことをポインタ変数という ポインタ変数は以下のように宣言できる 文法 データ型* ポインタ変数名; OR 文法 データ型 *ポインタ変数名; ここでのデータ型は、代入したアドレスにある変数のデータ型 メモリ 6 double b; int a; 6.0 int型ポインタ変数には int* pa; int型変数のアドレス のみ代入可能 008F3D29 第6章 ポインタ 5 ポインタ変数(2) 次のプログラムを実行しよう #include <stdio.h> int main(void){ int a; double b; int* pa = &a; double* pb = &b; printf("aのアドレスは%p,bのアドレスは%pです\n", pa, pb); return 0; } 実行結果は毎回異なります 第6章 ポインタ 6 間接参照(1) ある特定のアドレスにある、変数の値を取得することを 間接参照という メモリ 6 アドレス : 008F3D29 008F3D29にある変数の 値はなんだろう? 6だ! 間接参照には間接参照演算子を用いる。 演算子 意味 間接参照 * 優先順位 3 種類 単項 ポインタ変数の前に間接参照演算子を付けると値が取得できる 第6章 ポインタ 7 間接参照(2) 次のプログラムを実行しよう #include <stdio.h> int main(void){ int a; int* pa; pa = &a; a = 6; printf("アドレス%pにある変数の値は%dです\n", pa, *pa); return 0; } 初め pa = &a a = 6 a ??? ??? 6 &a 002FF730 002FF730 002FF730 第6章 ポインタ pa ??? 002FF730 002FF730 *pa ??? ??? 6 8 間接参照(3) 間接参照演算子を用いた代入も可能 int a 6 メモリ アドレス : 008F3D29 int* pa 008F3D29 ポインタ変数paに代入さ れてるアドレスに6を代 入しよう 結果的にaに6を代入して るのと同じだ *paの値を参照したり、代入したりするのは、 変数aの値を参照したり、代入したりするのと同じ *paとaは同じものと考えてもよい 第6章 ポインタ 9 間接参照(4) 次のプログラムを実行しよう #include <stdio.h> int main(void){ int a; int* pa; pa = &a; *pa = 6; printf("変数aの値は%dです\n", a); return 0; } 初め pa = &a *pa = 6 a ??? ??? 6 &a 002FF730 002FF730 002FF730 第6章 ポインタ pa ??? 002FF730 002FF730 *pa ??? ??? 6 10 間接参照(5) 次のプログラムを実行しないで下さい!! #include <stdio.h> int main(void){ int* pa; *pa = 6; return 0; } paに何が入ってるか分からない! ひょっとしたら、PCを動かすのに 重要な情報を保持している変数の アドレスが入っているかも 6を代入した瞬間にそのデータが 壊れる PCがおかしくなる たぶん安全装置が働きますが、念のためやめてね 第6章 ポインタ 11 ポインタのポインタ(1) ポインタ変数にも当然アドレスが存在します メモリ アドレス : 008F3D29 6 アドレス : 002FF730 008F3D29 当然、ポインタのアドレスを代入できる変数も存在します 文法 データ型** ポインタ変数名; OR 文法 データ型 **ポインタ変数名; 第6章 ポインタ 12 ポインタのポインタ(2) #include <stdio.h> int main(void){ int a; int* pa; int** ppa; pa = &a; ppa = &pa; **ppa = 6; printf("変数aの値は%dです\n", a); return 0; } 変数aと*paは同じものとして見てもよい またpaと*ppaも同じものとして見てもよい →*paと**ppaも同じものとして見てもよい 変数aと*paと**ppaは同じものとして見てもよい 第6章 ポインタ 13 ポインタのポインタ(3) #include <stdio.h> int main(void){ int a; int* pa; int** ppa; pa = &a; ppa = &pa; **ppa = 6; printf("変数aの値は%dです\n", a); return 0; } a &a pa *pa &pa 初め ? 2FF730 ??? ? pa=&a ? 2FF730 ppa=&pa ? **ppa=6 6 *ppa **ppa 3DA532 ? ? ? 2FF730 ? 3DA532 ? ? ? 2FF730 2FF730 ? 3DA532 3DA532 2FF730 ? 2FF730 2FF730 6 3DA532 3DA532 2FF730 6 第6章 ポインタ ppa 14 ポインタと関数(1) 変数aと*paは同じように使える! じゃあ別に変数aを使えば*paいらないよね? いらない文法があるわけありません! 変数aのアドレスさえ分かっていれば、 変数aのスコープ外であっても、変数aを編集できるのです その仕組みをよく使うのが関数です 第6章 ポインタ 15 ポインタと関数(2) 次のプログラムを実行しよう #include <stdio.h> //指定した変数に1を足す関数 void increment(int* num){ (*num)++; } int main(void){ int a = 10; increment( &a ); printf("%d\n",a); return 0; } これでもおんなじ結果に! でもこれだと出来ない プログラムもある!! ポインタ変数numに 変数aのアドレスが入る これを使えばスコープ外 でも変数aの編集が可能 #include <stdio.h> int increment(int num){ num++; return num; } int main(void){ int a = 10; a = increment( a ); printf("%d\n",a); return 0; } 第6章 ポインタ 16 ポインタと関数(3) 点(x,y)を原点中心にθ回転させた点(x2,y2)を取得する関数 を考える 戻り値がx2とy2の二つが必要 二つの戻り値を返す文法はない ポインタの出番 場所がないのでサンプルは次ページ 第6章 ポインタ 17 ポインタと関数(4) #include <stdio.h> #include <math.h> void rotation(double x,double y,double theta,double* x2, double* y2){ *x2 = x * cos(theta) - y * sin(theta); *y2 = x * sin(theta) + y * cos(theta); } int main(void){ double x, y, theta, x2, y2; printf("X座標 "); scanf("%lf",&x); printf("Y座標 "); scanf("%lf",&y); printf("角度 "); scanf("%lf",&theta); rotation(x, y, theta, &x2, &y2); printf("(%lf,%lf)---%lf(rad)回転→(%lf,%lf)\n", x, y, theta, x2, y2); return 0; } 第6章 ポインタ 18 ポインタと関数(5) さっきのページのプログラムで気づくことはなかったですか? scanf("%lf", &x); 第2引数では変数のアドレスを送ってあげます。 そうすると、変数の値が書き換えられるのです。 time関数もポインタを使って書くことが可能です time_t dateTime; dateTime=time(NULL); 同じ意味 ところでNULLってなんでしょう? time_t dateTime; time(&dateTime); time関数の本当の戻り値は time_t型 第6章 ポインタ 19 ヌルポインタ 次のプログラムを実行しよう #include <stdio.h> int main(void){ printf("%p\n",NULL); return 0; } NULLとはアドレス0のこと アドレス0は使わない決まりがある NULLが送られてきたら、time関数の中で 「おかしいから代入しないでおこう」ってなる 第6章 ポインタ 20 他のポインタの使い道 もし、音楽を代入できるデータ型と再生する関数があったら int型は4Byteだけど、音楽だと数十Mbyteになりそう 再生するたびに引数にそのデータをコピー さすがに数十Mbyteもあるとコピーに時間がかかる アドレスをコピーするだけなら数Byteで済む ポインタを使おう!! 第6章 ポインタ 21 ここまでのまとめ 変数の保存されてる場所をアドレスという アドレスを保存できる変数をポインタ変数という 間接参照演算子で間接参照、代入が可能 ポインタで関数の引数を戻り値のような役割にできる 重いデータはポインタのコピーだけで済ます 第6章 ポインタ 22 練習問題 問1 int型変数a,bのアドレスを引数として送ると、 a,bの値が入れ替わる関数を作れ 問2 点(x,y)をx軸方向にa,y軸方向にb平行移動した 点(x2,y2)を取得する関数を作れ 第6章 ポインタ 23
© Copyright 2024 ExpyDoc