プログラミング入門2 第10回 ポインタ 芝浦工業大学情報工学科 青木 義満 ポインタがオブジェクトを「指す」 ポインタptの値がオブジェクトnxのアドレスであるとき, pt は nx を指す という。 57 メモリ上の番地 int nx; int *pt; pt = &nx; 1000番地 = nx &nx 代入 nxのアドレス pt ptにはオブジェクトnxのアドレスが格納されているので, ptはnxを指している といえる。 プログラミング入門2 2 ポインタとエイリアス(別名) int nx; int *pt; nx = 57; pt = &nx; pt は x を指す *pt はptが指すオブジェクトの エイリアス(別名) x x pt *pt pt *ptはxの別名(エイリアス) 重要 ポインタpがオブジェクトxを指すとき,*pはxのエイリアス(別名)となる プログラミング入門2 3 今回の講義内容 ポインタと関数 ポインタと配列 プログラミング入門2 4 ポインタと関数 ソースファイル名:list1004-1.c 関数の引数とポインタ(間違い) #include <stdio.h> void hiroko(int height) { if ( height < 180) height = 180; } 身長を180cmに伸ばしてくれる関数 Height before : 179 Height after : 179 int main(void) { int masaki = 179; printf("height before:%d\n", masaki); 値が変更されていない ! hiroko(masaki); printf("height after:%d\n", masaki); return (0); } プログラミング入門2 5 値の受け渡し 実引数の値が仮引数の値にコピーされるだけ #include <stdio.h> void hiroko(int height) { if ( height < 180) height = 180; } ・heightには179という値がコピーされる ・関数内でheightの値は変更されるが, main文の方のmasakiの値は変更されない! int main(void) { int masaki = 179; printf("height before:%d\n", masaki); hiroko(masaki); 直接値を変更できない ↓ ポインタを利用して,間接的に変更! printf("height after:%d\n", masaki); return (0); } プログラミング入門2 6 ポインタと関数 ソースファイル名:list1004-2.c 関数の引数とポインタ(正解) #include <stdio.h> void hiroko(int *height) { if ( *height < 180) *height = 180; } 身長を180cmに伸ばしてくれる関数 Height before : 179 Height after : 180 int main(void) { int masaki = 179; printf("height before:%d\n", masaki); 値が変更OK! hiroko(&masaki); printf("height after:%d\n", masaki); return (0); } プログラミング入門2 7 関数の引数としてのポインタ ポインタを介して,間接的に変数の値を変更 int masaki; masaki = 179; masakiのアドレス(&masaki) を関数に渡す hiroko(&masaki); int *height; height = &masaki; 106番地 void hiroko(int *height) { if ( *height < 180) *height = 180; } *heightはmasakiのエイリアス 106番地 179 masaki 179 *height 180 height プログラミング入門2 8 関数におけるポインタの使用例(p.234) ソースファイル名:list1005-1.c 関数の引数とポインタ(間違い) #include <stdio.h> void swap(int nx, int ny) { int temp = nx; nx = ny; ny = temp; } int main(void) { int na, nb; temp nxの値を一時的に tempに格納 nx nyにtemp(元のnxの値) を代入 ny nyの値をnxに代入 puts("二つの整数を入力してください。"); printf("整数A:"); scanf("%d", &na); printf("整数B:"); scanf("%d", &nb); swap(na, nb); 変数の値を関数を使って変更したい → ポインタを使いましょう puts("これらの値を交換しました。"); printf("整数Aは%dです。\n", na); printf("整数Bは%dです。\n", nb); return (0); } プログラミング入門2 9 関数におけるポインタの使用例(p.234) ソースファイル名:list1005-2.c 関数の引数とポインタ(正解) #include <stdio.h> void swap(int *nx, int *ny) { int temp = *nx; *nx = *ny; *ny = temp; } int main(void) { int na, nb; puts("二つの整数を入力してください。"); printf("整数A:"); scanf("%d", &na); printf("整数B:"); scanf("%d", &nb); swap(&na, &nb); puts("これらの値を交換しました。"); printf("整数Aは%dです。\n", na); printf("整数Bは%dです。\n", nb); return (0); } プログラミング入門2 10 関数とポインタの重要ポイント 関数に対して,変数の値の変更を頼みたい時 関数にその変数へのポインタ(その変数のアドレス) を渡す ポインタ(オブジェクトのアドレス)を渡して, 関数側はそのポインタが指すオブジェクトに対して処理を行う hiroko(&masaki); void hiroko(int *height) プログラミング入門2 11 関数におけるポインタの使用例(p.236) ソースファイル名:list1007.c 2つの整数の和と差を求める #include <stdio.h> void sum_diff( int n1, int n2, int *sum, int *diff ) { *sum = n1 + n2; *diff = (n1 > n2) ? n1 - n2 : n2 - n1; } int main(void) { int na, nb; int wa = 0, sa = 0; puts("二つの整数を入力してください。"); printf("整数A:"); scanf("%d", &na); printf("整数B:"); scanf("%d", &nb); sum_diff( na, nb, &wa, &sa ); 何故,na, nbには&が不要? printf("和は%dです。\n差は%dです。\n", wa, sa); return (0); } プログラミング入門2 12 ポインタを使うか使わないか 値を変更する必要があれば,ポインタで受け渡し そうでなければ,ポインタを使用する必要なし (例えば,その値を計算にしようするだけ などの場合) void sum_diff( int n1, int n2, int *sum, int *diff ) { *sum = n1 + n2; *diff = (n1 > n2) ? n1 - n2 : n2 - n1; } n1, n2 は和と差を求めるために使用するだけ →値だけ使えればよい(ポインタを使う必要なし) sum, diff は変数のアドレスを受け取り,和と差を求めた結果を代入 →値を変更する必要がある(ポインタを使う必要あり) プログラミング入門2 13 値の並べ替え(ソート,p.237) ソースファイル名: list1008.c 2つの整数値を小さい順に並べる(昇順ソート) #include <stdio.h> /*--- nx・nyが指すオブジェクトの値を交換 ---*/ int main(void) { int void swap(int *nx, int *ny) puts("二つの整数を入力してください。"); printf("整数A:"); scanf("%d", &na); printf("整数B:"); scanf("%d", &nb); { int temp = *nx; *nx = *ny; *ny = temp; sort2( &na, &nb ); } puts("これらの値を昇順に並べました。"); printf("整数Aは%dです。\n", na); printf("整数Bは%dです。\n", nb); /*--- *n1≦*n2となるように並べる ---*/ void sort2(int *n1, int *n2) { if (*n1 > *n2) swap(n1, n2); } na, nb; return (0); } *n1, *n2, n1, n2には何の値が入っている? プログラミング入門2 14 scanf関数とポインタ printf関数とscanf関数 printf(“x = %d\n”, x); scanf(“%d”, &x); printfはただ変数の値を表示するだけ 変数の値を変更する必要なし → そのまま変数を渡す scanfは,キーボードから読み込んだ値を変数に格納する 変数に値を代入(値を変更)する必要 → ポインタ(アドレス)で渡す! プログラミング入門2 15 ポインタと配列 (p.240) ソースファイル: list1010.c 配列とポインタ #include <stdio.h> int main(void) { int i; int vc[5] = {10, 20, 30, 40, 50}; int *ptr = &vc[0]; for (i = 0; i < 5; i++) printf("vc[%d] = %d ptr[%d] = %d *(ptr + %d) = %d\n", i, vc[i], i, ptr[i], i, *(ptr + i) ); return (0); } プログラミング入門2 16 配列とポインタ 配列 vc の先頭要素vc[0]のアドレス &vc[0] を ptrに代入 int *ptr = &vc[0]; ptr + i Ptrが指すオブジェクトのi個後ろの要素 を指すポインタ *(ptr + i) Ptrが指すオブジェクトのi個後ろの要素 のエイリアス *(ptr + i) = ptr[i] vc[0] *ptr ptr[0] vc[1] vc[2] *(ptr+1) ptr[1] *(ptr+2) ptr[2] vc[3] vc[4] *(ptr+3) ptr[3] *(ptr+4) ptr[4] ptr ポインタに対して [ ]を適用すること で,配列のように扱える!! プログラミング入門2 17 配列とアドレス (p.242) ソースファイル: list1011.c 配列のアドレスを表示 #include <stdio.h> int main(void) { int vc[3]; printf("vc :%p\n", vc); printf("vc[0]のアドレス:%p\n", &vc[0]); printf("vc[1]のアドレス:%p\n", &vc[1]); printf("vc[2]のアドレス:%p\n", &vc[2]); return (0); } 添え字演算子[ ]を使わずに単独に現れた配列名は, その配列の先頭要素へのポインタとみなされる プログラミング入門2 18 配列の受け渡し (p.244) ソースファイル名:list1013.c 関数への配列の受け渡し(復習) #include <stdio.h> void int_set( int intvc[ *vc] ) { int i; for (i = 0; i < 5; i++) vc[i] = 0; } 復習 関数で配列を受け取る時, int vc[ ] → int型のオブジェクトのアドレスを受け取る = int *vc で受け取り可能! int main(void) { int i; int ary[ ] = {1, 2, 3, 4, 5}; int_set( ary ); 配列名(ary) =配列の先頭要素のアドレス =&(ary[0]) int型のオブジェクトのアドレスを渡す for (i = 0; i < 5; i++) printf("ary[%d] = %d\n", i, ary[i]); return (0); } プログラミング入門2 19 配列の受け渡し 関数間での配列の受け渡し 先頭要素へのポインタで行う 受け取り側の関数は,そのポインタに対して添え字演算子 [ ]を適用する ことで,配列のようにアクセスできる int_set( ary ); 500番地 502番地 504番地 506番地 508番地 ary[0] *vc vc[0] ary[1] *(vc+1) vc[1] ary[2] *(vc+2) vc[2] ary[3] *(vc+3) vc[3] ary[4] *(vc+4) vc[4] &ary[0] (500番地) void int_set( int *vc ) int型オブジェクトのアドレスを受け取るので, ポインタ(int *)で宣言 vc プログラミング入門2 20 scanf関数とポインタ scanfは,キーボードから読み込んだ値を変数に格納する 変数に値を代入(値を変更)する必要 → ポインタ(アドレス)で渡す! 文字列の場合 文字配列の先頭アドレスを渡してやればよい &をつけずに,配列名でOK char name[256]; name = &(name[0]) scanf( “%s”, name ); プログラミング入門2 21 二次元配列の関数への渡し方 ソースファイル名: 2jigen.c 関数への二次元配列データの受け渡し #include <stdio.h> void print_name( char x[ ][256] ) 先頭の要素数は省略可能 { int i; for(i=0; i < 4; i++){ printf("name[%d] = %s\n", i, x[i] ); } } int main(void) { char name[4][256]={"aoki", "morishima", "tokunaga", "yanagisawa" }; print_name( name ); 配列名で関数へ受け渡し return(0); } プログラミング入門2 22 演習課題 ファイル名:kadai10-1.c ポインタと関数,配列 3名分の氏名,身長,体重データが格納されている配列を受け取って,肥満度(BMI 値)を計算し,全員の肥満度を表示する関数を設計せよ 氏名、身長、体重、BMI値を格納する配列はMain関数で宣言、関数にそれらのデータ を渡して関数内でBMI値を計算して配列BMIに結果を格納。 Main関数中でBMI値に基づき、判定結果を表示。 氏名,身長,体重のデータはキーボード読込み,ファイル読込みのどちらでも良い。 void calculate_bmi( char name[ ][256], double *height, double *weight, double *bmi ) 名前の配列 身長の配列 体重の配列 BMIの配列 <BMI(Body mass index)の計算方法> 身長の二乗に対する体重の比で体格を表す指数 BMI=体重(kg)/(身長m)2 BMI 25以上 BMI 18.5以上25未満 BMI 18.5未満 → 肥満 → 標準体重 → 低体重 プログラミング入門2 23 演習課題 ファイル名:kadai10-2.c 要素数noのint型配列データを受け取り,値の小さい順に要素を並べ替える(ソート)関数を設計し, その動作を確認せよ。なお,並べ替えのアルゴリズムは以下の手順に従うこと。(他のアルゴリズムでも良い) void select_sort( int *vc, int no ) セレクトソート法(最も単純なソート法) ※3, 5, 2, 4, 1 というデータを例に説明 A.まず、データ全体の中から、最小の値を探す B.探し出した最小の値と、データの先頭の値を、入れ替える C.次に、先頭から2番目より最後までの値の中から、最小の値を探す D.探し出した最小の値と、先頭から2番目の値の値を、入れ替える E.同様に、先頭から3番目より最後までの値の中から、最小の値を探す F.探し出した最小の値と、先頭から3番目の値の値を、入れ替える G.最後まで処理を繰り返す この結果、順番が1, 2, 3, 4, 5 と並び替えられる 3, 5, 2, 4, 1 1, 5, 2, 4, 3 1, 5, 2, 4, 3 1, 2, 5, 4, 3 1, 2, 5, 4, 3 1, 2, 3, 4, 5 提出期限: 12月17日(月)13:00(厳守) 提出先: [email protected] メールのタイトル:pro2-10 学籍番号+自分の苗字 例) pro2-10 L02001 aoki プログラミング入門2 24
© Copyright 2024 ExpyDoc