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]
© Copyright 2024 Paperzz