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