240 likes | 383 Views
プログラミング講義資料. 情報社会学科 (IS 進学予定者). 今週の内容. ポインタ. イントロダクション (1). 関数を習ったが、関数に渡せるのは 変数の「値」だけ 値渡し. 関数の中では、渡された「値」が呼び出し側で どんな変数に入っていたかは知るよしもない. 関数でできるのは 値を 1 つ返すこと 大域変数を書き換えること 出力などの副作用的な処理. これ以外は main 関数には伝わらない. イントロダクション (2). でも、 関数から 2 つ以上の値を返したい 関数から配列の値をまとめて返したい
E N D
プログラミング講義資料 情報社会学科(IS進学予定者)
今週の内容 • ポインタ
イントロダクション(1) • 関数を習ったが、関数に渡せるのは 変数の「値」だけ値渡し • 関数の中では、渡された「値」が呼び出し側で • どんな変数に入っていたかは知るよしもない • 関数でできるのは • 値を1つ返すこと • 大域変数を書き換えること • 出力などの副作用的な処理 これ以外はmain関数には伝わらない
イントロダクション(2) でも、 • 関数から2つ以上の値を返したい • 関数から配列の値をまとめて返したい • main関数でも利用している変数や配列などを、関数の中で書き換えちゃって欲しい ポインタ変数を引数にすれば可能
特別演算子 • アドレス演算子とポインタ演算子 • C言語ではメモリのアドレスや内容を演算子として利用できるアドレス、ポインタ アドレス演算子 & 例 : &x (変数xのアドレス) ポインタ演算子 * 例 : *x (アドレスxの内容) • 関数内で値を書き換えられる • 配列を関数に渡したり、配列を関数から返したりできる
プロトタイプ宣言 * に注目 void printnum(int *); だけでもいい 関数の呼び出し & に注目 変数aのアドレスがわたっている 仮引数 num * に注目 *num で ポインタが指しているアドレスの 中身が参照される ポインタ引数 参照渡し 関数にポインタを引数として渡すことができる #include <stdio.h> void printnum(int *num); int main(void) { int a = 10; printnum(&a); return 0; } void printnum(int *num) { printf("%d\n", *num); return; }
‘a’という名前はローカル変数の名前であり、関数printnumからは見れない。でも、アドレスが分かれば、名前なんか分からなくても読み書きできる。‘a’という名前はローカル変数の名前であり、関数printnumからは見れない。でも、アドレスが分かれば、名前なんか分からなくても読み書きできる。 8x00070 printnum(int *num) main printf(“%d\n”,*num); int a=10; printnum(&a); メモリ 10 a 8x00070 アドレス
参照渡しの効果 関数で複数の値を求めて呼び出し側に伝えることができる 関数wasekiの実行が終わって帰ってくると、 変数 wa に a+b が入っていて、 変数 seki に a*b が入っている #include <stdio.h> void waseki(int, int, int *, int *); int main(void) { int a=10, b=7, wa=0, seki=0;waseki(a, b, &wa, &seki); printf(“a=%d, b=%d, a+b=%d,a*b= %d\n”,a,b,wa,seki); return 0; } void waseki(int a, int b, int *add, int *mult) { *add = a+b; *mult = a*b; return; } 和を入れてもらう変数へのポインタ(アドレス) 積を入れてもらう変数へのポインタ(アドレス) 和を入れてあげる変数へのポインタ(アドレス) 積を入れてあげる変数へのポインタ(アドレス) addがポイントする変数にa+bを代入 multがポイントする変数にa*bを代入
参照渡しの効果 関数に配列を渡すことができる aは関数”sum”のローカル変数として定義されたポインタ変数であり、mainの中でのaとは違う! #include <stdio.h> int sum(int *, int); int main(void) { int a[10];/* a[0] から a[9] までの入力は省略 */ printf(“合計は %d です”,sum(a,10)); return 0; } int sum(int *a, int num) { int i; int wa=0; for (i=0, i<num, i++) { wa=wa+*a; a++ } return wa; } 配列の名前、すなわち、先頭へのポインタ 1回目a は配列の先頭へのポインタ*a は配列の先頭の要素 a++で aは次の要素を指すことになる
int *a は int a[] と書いても良い。 #include <stdio.h> #define STUDENTS 10 float average(int [ ], int);/*平均は小数なので、戻り値は int じゃないよね。*/ int main(void) { int score[STUDENTS] = {58, 46, 72, 47, 89, 78, 62, 91, 68, 78}; float ave; /* 平均値 (average) */ ave = average(score, STUDENTS); printf("average = %f\n", ave); return 0; } /* n 個の整数の平均を求める関数*/ float average(int array[ ], int n) {int i; float x = 0; /* 平均値 */ for (i = 0; i < n; i++) { x = x + array[i]; } return x / n; }
参照渡しの効果 関数に文字列を渡すことができる #include <stdio.h> void printstr(char *); int main(void) { char str [ ] = "A quick brown fox jumped over the lazy dog."; printstr(str); Return 0; } /* たてがきくん */ void printstr(char *str) { while (*str != '\0') { printf("%c\n", *str); str++; } return; }
ポインタの戻り値 • 関数にポインタを渡すことができる • じゃあ、 関数からポインタを返すこともできる? できる!!
ポインタの戻り値 関数“first_a”がポインタを返すことをあらわす‘*’ char *first_a(*str) { while (*str != ‘\0’) { If (*str == ‘a’) { break; } str++; } if (*str == ‘\0’) { return NULL; } else { return str; } } (このポインタはchar型 のデータを指すポインタ) (3種類の‘*’の意味の 違いに注意!) 文字配列(文字列)のポインタを渡され、 最初に出現する ‘a’ を指すポインタ を返す。 文字列に ‘a’ が出現しないときは、 NULL を返す。
課題 • 例題ex10a • 以下のプログラムは、文字配列を表示する関数を、配列名を引数にした場合とポインタを引数にした場合で実装したものである。 • 実行の過程を追って実引数と仮引数の値をプリントしているので、適当にプリント文を増やしてそれぞれの変化の様子を確認せよ。 • 引数の値は通常の値渡しであり、仮引数から実引数に戻らないことに注目せよ。
#include <stdio.h> #define LENGTH 50 void printstr_a( char str[ ] ); /* 配列を引数とする*/ void printstr_p( char *str ); /* ポインタを引数とする*/ int main( void ){ char comment[LENGTH+1] = "My programming skill has improved."; printf( "\n*** print results with an array ***\n" ); printf( "&comment[0]-in = %d\n", &comment[0] ); printstr_a( &comment[0] ); printf( "&comment[0]-out = %d\n", &comment[0] ); printf( "\n*** print results with a pointer ***\n" ); printf( "comment-in = %d\n", comment ); printstr_p( comment ); printf( "comment-out = %d\n", comment ); return 0; }
/* 配列名を引数とする関数*/ void printstr_a( char str[ ] ){ int i = 0; printf("printstr_a &str[0]-in = %d\n",&str[0]); while ( str[i] != '\0' ) { printf( "%c", str[i] ); i++; } printf( "\n" ); printf("printstr_a &str[0]-out = %d\n",&str[0]); return; }
/* ポインタを引数とする関数*/ void printstr_p( char *str ){ printf("printstr_p str-in = %d\n",str); while ( *str != '\0' ) { printf( "%c", *str ); str++; } printf( "\n" ); printf("printstr_p str-out = %d\n",str); return; }
例題ex10a実行結果 *** print results with an array *** &comment[0]-in = 1244916 printstr_a &str[0]-in = 1244916 My programming skill has improved. printstr_a &str[0]-out = 1244916 &comment[0]-out = 1244916 *** print results with a pointer *** comment-in = 1244916 printstr_p str-in = 1244916 My programming skill has improved. printstr_p str-out = 1244950 comment-out = 1244916
課題 • 例題ex10b • 以下のプログラムはint型配列の各要素のアドレスと内容を表示するプログラムである。 • 例題ex10aにおける、ポインタを引数とする関数の仮引数の傾向と異なる理由について考察せよ。また、他の型(float, double)ではどうなるか考えよ。
#include <stdio.h> #define AMAX 10 void printint_p(int *p); int main(){ int a[AMAX]; int i; for(i=0;i<AMAX;i++){ a[i]=i; } printint_p(a); return 0; } void printint_p(int *p){ int i; for(i=0;i<AMAX;i++){ printf("%d : %d\n", p, *p); p++; } }
例題ex10b実行結果 1244932 : 0 1244936 : 1 1244940 : 2 1244944 : 3 1244948 : 4 1244952 : 5 1244956 : 6 1244960 : 7 1244964 : 8 1244968 : 9
課題 • 課題rp10a • 整数配列にストアした数列値を降順に並び替えてから出力するプログラムを、以下の関数を使って作成せよ。 • void bsort(int *A, int k); • 引数で渡す配列をソートする • int *A : 対象の配列のポインタ • int k : 対象配列の要素数 • void swap_int(int *a, int *b) • int型の数値を入れかえる
課題 • 課題rp10b • 入力された2つの文字列str1,str2を比較し、str1にstr2の文字列が含まれている場合に1を、含まれていない場合に-1を返す関数、strcmpを作成せよ。 • int strcmp( char *str1, char *str2); • ex. str1=”abaabab” , str2=”aab” 1 • ex. str1=”abaabab” , str2=”abb” -1
これからやること • 例題ex10a,ex10bの確認 • 課題rp10a,rp10bの作成 • レポートの作成・提出