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