プログラミング演習II 2004年11月 2日(第3回) 理学部数学科・木村巌
前回までの復習 関数の宣言 複数のファイルからなるプログラム 複数のファイルからなるプログラムでのスコープ 標準ライブラリ関数とは何か 標準ライブラリ関数の使い方
今日学ぶこと メモリとアドレス ポインタ変数 アドレス演算子 &, 間接参照演算子 * ポインタを引数とすることで、呼び出し元の実引数を変更できる constを仮引数に付けることで、実引数の変更を抑制できる 教科書9.1から(p. 272~)
アドレスって? 変数の値は、コンピュータの「メモリ」に記憶される メモリには、16進表記で通し番号が振られている この通し番号を、アドレス(address)という
変数のアドレスを見る 変数が格納されているメモリのアドレスをしるには、アドレス演算子&を使う アドレス演算子の使い方 &変数名 例えば、aがint型の変数なら、そのアドレスは&a で知ることが出来る アドレスを端末に表示するには、%p 書式指定子を使う(Sample1.cを打ち込んで、コンパイル・実行してみよう)
アドレスは、メモリ上の位置 アドレス演算子を使うと、変数aの値が、メモリ上のどの位置に記憶されているか、が分かる Sample1.cで、さらにint型の変数b, cを追加し、それらのアドレスも表示させてみよ。 気がつくことがあるか?
ポインタ アドレスを格納する変数を、ポインタ型の変数という ポインタ型の変数の事を、単にポインタということも多い ポインタ型の変数の使い方は、これまで学んできた、他の型の変数と同じ
ポインタの宣言 型名 *ポインタ変数名; ポインタ変数であることが分かるような名前を付けることも多い 例: 変数名はあくまでpAとAptr int *pA; int *Aptr; /* どちらもint型へのポインタ */ 変数名はあくまでpAとAptr Sample2.cを入力し、コンパイル・実行してみよう
ポインタ変数にアドレスを代入 Sample2.cの pA = &a; という行は、ポインタ変数pAに、int型の変数aのアドレスを代入している pAには、例えば0x22ef3c といった値が入る 「pAはaを指すポインタ」などという
ポインタから変数の値を知る ポインタ変数の値(つまり、別の変数のアドレス)から、元の変数の値を知ることが出来る 間接参照演算子*をつかう *ポインタ変数名; 例:pAがint型の変数aのアドレスを格納したポインタ変数なら、aの値は、 *pA /* これはint型 */ Sapmle3.cを入力し、コンパイル・実行してみよう
ポインタについて整理する a 変数a &a 変数aのアドレス pA 変数aのアドレスを格納したポインタ変数 *pA
ポインタに別のアドレスを代入する ポインタ変数とは、アドレスを格納する変数 普通の変数と同じく、プログラムの途中で、ポインタ変数の値を変更することが出来る ただし、int型を指すポインタ変数には、int型の変数のアドレスしか格納できない Sample4.cを入力し、コンパイル・実行してみよう
初期化されていないポインタ変数 ポインタ変数は、宣言しただけでは使えない 特定の変数のアドレスを代入し、初期化しなければならない 初期化していないポインタ変数を使うと、ゴミが表示されたり、実行時エラーになったりする(どのような挙動を示すかは、予言できない
ポインタを使って変数を変更 Sample5.cを入力し、コンパイル・実行してみよう pAがint型を指すポインタ変数の時、*pAはint型.よって、*pA = 50; のように、代入できる この仕組みを使って、関数に引数を渡すと、面白い応用がある
引数とポインタ int型の2つの値を交換する関数void swap (int x, int y)を書きたい →は思った通りには動かない int tmp; tmp = x; x = y; y = tmp; }
なぜ前のswap()は動かないのか? Sample6.cを入力し、コンパイル・実行してみよう→思ったように値が交換されない 関数の引数には、変数の値のコピーが渡される これを、値渡し(pass by value)とか、値呼び(call by value)とかいう コピーを交換しても、元の変数には影響が及ばない
ポインタを使って解決 仮引数をポインタ変数とする void swap (int *pX, int *pY) →参照 { Sample7.cを入力し、コンパイル・実行してみよう void swap (int *pX, int *pY) { int tmp; tmp = *pX; *pX = *pY; *pX = tmp; }
ポインタを使って解決(続) 前のスライドをswap()を使う。 引数として、&num1, &num2のように、変数のアドレスを渡す pX = &num1; pY = &num2; *pX = num1; *pY = num2; これらが入れ替えられると、num1, num2そのものが入れ替えられる 参照渡し(pass by reference)
実引数を変更したくない場合 constという指定で、ポインタがさす実引数を変更できなくなる void func (const int *pX); というプロトタイプ宣言をみると、この関数はpXが指す変数を変更しないことがわかる 同時に、func()を作成する際に、誤ってpXが指す変数を変更してしまっても、コンパイラがその誤りを指摘してくれる
今日学んだこと メモリとアドレス ポインタ変数 アドレス演算子 &, 間接参照演算子 * ポインタを引数とすることで、呼び出し元の実引数を変更できる constを仮引数に付けることで、実引数の変更を抑制できる
レポート課題 void mod (int *pA, int *pM) というプロトタイプを持つ関数mod()は、*pAを (*pA) % (*pM)に置き換える.キーボードからint型の値aとmを入力し、次のように出力するプログラムを作成せよ: ヒントは教科書p. 298の練習問題3.
レポート課題(続) input a and m: 7 (ユーザの入力) 5 (ユーザの入力) 7 (ユーザの入力) 5 (ユーザの入力) 7 modulo 5 = 2 modulo 5.
レポート課題(続) 締め切り:2004年11月8日一杯(日本時間で) 提出:メールで木村(iwao@sci.toyama-u.ac.jp)まで. 感想などあると木村が喜びます