プログラミング言語Ⅰ演習 第11回

1
2
本日の内容
プログラミング言語Ⅰ演習
第11回
講義担当:
稲元 勉 (いなもと つとむ)
阿萬 裕久 (あまん ひろひさ)
• ポインタ(pointer)の基礎
• 関数の引数とポインタ
• 配列とポインタの関係
最初に10分ほど簡単
に説明しますので,
その後,各自のペー
スで演習に取り組ん
でください
3
4
ポインタとは何か
プログラムの場合:対象の位置?
• ポインタ(pointer)
• ここではまず
何かの位置を指し示すもの
「対象」 = 「変数」(※int 型とか)
と考えることにしましょう
• その位置(変数の位置)とは
例えば,マウスだとマウス操作(クリックやドラッグ)
対象の位置を上のようなアイコンで表示している:
「マウスポインタ」と呼ばれる
変数の番地(アドレス)
5
6
ちょっと専門的な補足説明(1)
ちょっと専門的な補足説明(2)
• コンピュータの中のメモリ(記憶装置)は,
• 例えば,ある int 型の
番地
1バイト(8ビット)単位で細かく区画整理
されています
番地 区画
変数 x を宣言すると
0
0
2
「番地」が割り振られて
います
3
4
5
2
変数 x のため
に 4 区画(4バ
イト)を確保!
3
4
5
6
右上の例の場合,変数 x は 2 番地から これが変数の
位置情報!
4 バイト分を占有していることになる
・
・
・
6
int x;
・
・
・
• 各区画には住所として
1
メモリ上のどこか空いて
いる場所にそのための
場所が確保されます
1
区画
(ポインタで扱う)
7
8
実際の番地
変数の番地を調べる方法
• 実際には番地は16進数で表現されます
• 変数名を x としたとき
(例)
0xbfd2db5c
最初の 0x は
16進数であ
ることを表現
している
10進数で言えば
3218266972番地
(全部で32ビット)
演習課題(1)
• 番地は変数名の前に &(アンパサンド)を付ける
&x
• printf で表示する場合は “%p” を使用
printf(”%p ¥n”, &x);
9
10
変数の占有バイト数を調べる方法
ポインタ変数とは
• 変数名を x としたとき
• 番地を記録するために用意された特別な
演習課題(1)
変数をポインタ変数といいます
• sizeof(…) で変数を囲む
sizeof(x)
• ただし,そのポインタが指し示す先にどの
• printf で表示する場合は
種のデータがあるのかが分からないと
困ってしまうので,その情報も必要
printf(”%d ¥n”, sizeof(x));
int 型? char 型? double型? ・・・
それぞれ占有バイト数も表現形式も違う!
11
12
ポインタ変数の宣言方法
ポインタ変数の使用例
• 型名と変数名の間に*(アスタリスク)を付け
• int 型変数 x の番地をポインタ変数 p に
ます
int *p;
• こうすると p という名前のポインタ変数が
用意されます
• ただし,この変数 p に代入してよいのは
int 型変数の番地のみであることに注意
して下さい
代入し,それを表示する
int x;
int *p;
p = &x;
printf(”%p ¥n”, p);
x は普通の int 型
変数で,p にその
番地(&x)を代入
している
13
14
ポインタ変数を使ったデータアクセス
少し解説すると
• ポインタ変数を使って,対象の値を読み
• 変数 x の番地が 0x00112233 として
書きする場合
• ポインタ変数の直前に * を付ける
p = &x;
printf(”%d ¥n”, *p);
int x;
int *p;
p = &x;
ポインタ変数 p には 0x00112233 が入っている.
このとき 「*p」 は 「0x00112233 番地に置いてある
変数」という意味になる.
printf(”%d ¥n”, *p);
変数を人間に例えると,それぞれ「名前」があるが,それとは別に「前から3
番目左から4番目に座っている人」という表現方法もある(後者がポインタ)
15
当然書き込みもできる
int x;
int *p;
p = &x;
x = 1;
printf(”%d ¥n”, *p);
16
ポインタが何の役に立つのか?
演習課題(2)
最初は 1 が入っ
ているが,後か
ら 3 が代入され
ている
• そもそもこんなことをして何の役に立つの
か?という疑問を持つ人も多いと思いま
す
• その疑問に答えるために次の例を考えて
みましょう
*p = 3;
printf(”%d ¥n”, *p);
17
関数呼び出しでの落とし穴
関数呼び出しの引数は「コピー」
• 以下の実行結果は 「5」 になりません
void foo( int x ){
x = 5;
}
int main(void){
int x = 0;
foo(x);
printf(”%d¥n”, x);
18
演習課題(3)
void foo( int x ){
x = 5;
}
x
x
0
5
コピー
メモリ管理は
それぞれ
独立している
int main(void){
int x = 0;
x 0
foo(x);
printf(”%d¥n”, x);
単にコピーした先を書き換
えているだけなので,元の
return 0;
方には反映されない!
}
結果は「0」になる!
return 0;
}
19
ポインタを使うことで解決
関数を使って変数を書き換える
演習課題(4)
void foo( int *p ){
*p = 5;
}
20
p
番地をコピー
• 呼び出す側:
書き換えたい変数の番地を渡す
関数名(&変数名);
int main(void){
int x = 0;
foo(&x);
printf(”%d¥n”, x);
return 0;
}
x
0
5
「5」に書き
換わる!
• 呼び出される側:
(例) foo(&x);
※だから scanf では & を付けるんです!
scanf を使うと読み込んだ値が書き込まれる
ポインタ変数を使って目的の変数にアクセス
関数名(型名 *ポインタ変数){
(*ポインタ変数) = …
}
(例)*p
= 5;
21
22
ポインタと配列の関係
試してみましょう
• ポインタとは配列の間には密接な関係が
• 配列を用意して,その番地と占有メモリ
あります
数を表示させてみましょう
int a[3];
int a[3];
• 実はこのとき,a は配列の先頭番地を表
すポインタになっているんです
printf(”%p¥n”, a);
printf(”%p¥n”, &a[0]);
printf(”%p¥n”, &a[1]);
printf(”%p¥n”, &a[2]);
printf(”%d¥n”, sizeof(a));
printf(”%d¥n”, sizeof(a[0]));
演習課題(5)
• a[0] の番地が
ポインタ a と
同じであること
• 配列の要素間
での番地の差
が変数の占有
バイト数で決
まっていること
をそれぞれ確認
23
24
配列名 ≠ ポインタ変数
ポインタ変数を使った配列内アクセス
• 配列の名前は一種のポインタ変数として
• ポインタ変数を使って配列内の要素を順
見ることができますが,その内容を書き
換えることはできません
int a[3];
int b[10];
int *p;
に見ていくことができます
int a[3];
int *p;
p = a;
printf(”%d¥n”, *p);
p = a;
問題ない
a = b;
間違い!
p++; あるいは p = p + 1;
printf(”%d¥n”, *p);
a[0] の内容が表示
される
a[1] の内容が表示
される
25
26
p++ や p = p+1 ならば番地を+1では?
やってみよう
• 番地を「+1」したはずなのですが,先の
• うまく int 型変数1個分ずつずれているか
方法でうまく表示されるようになります
• int 型の変数は1バイトでは無かったはず
…. → どうして?
• ポインタ変数が int 型用に宣言されてい
るので,自動的に int 型変数1個分だけ
番地をずらすようになっているんです
確認
演習課題(6)
int a[3];
int *p;
p = a;
printf(”%p¥n”, p);
p++;
printf(”%p¥n”, p);
p++;
printf(”%p¥n”, p);
printf(”%d¥n”, sizeof(a[0]));
27
28
その他:配列を関数の引数にする場合
さらにできる人は(加点の対象)
• 配列の場合,配列名が既に番地になって
演習課題(7)では配列の中を0,1,2,・・・と
順に埋める関数 setup を作成することに
なっている
さらにできる人はこれを改良し,順に素数
を埋める関数 prime を作りなさい.
• 素数=1とその数以外では割り切れない
2以上の整数
プログラムをメールで提出
いるので「&」は不要である
void foo( int a[] ){
a[0] = 5;
a[1] = 3;
}
int main(void){
int a[3];
foo(a);
…
演習課題(7)
配列の長さは
書かなくてもよい
2,3,5,7,11,・・・
【提出先】 (〆切:来週水曜)
[email protected]