スライド 1

第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