関数とポインタ 値呼び出しと参照呼び出し swapのいろいろ 関数引数 数値積分 第11章 関数について 関数とポインタ 値呼び出しと参照呼び出し swapのいろいろ 関数引数 数値積分
10.7 関数とポインタ 関数名はポインタ 戻り値がポインタの関数 引数がポインタの関数 アドレスはプログラム領域 メモリ管理関数(malloc, calloc,...) 文字列処理関数(第13章、第15章) 配列や構造体を戻す関数 引数がポインタの関数 参照呼び出し(call by reference) scanfなど ⇔値呼び出し(call by value) 値をコピーして渡す
値呼び出しと参照呼び出し さて、 x と y の値がちゃんと交換されるのはどちらのプログラムだろうか。 #include <stdio.h> void swap(int x, int y) { int temp; temp=x; x=y; y=temp; printf("&x=%u:x=%d\n", &x,x); printf("&y=%u:y=%d\n", &y,y); } int main(void) int x=5, y=3; swap(x, y); printf("&x=%u:x=%d¥n", &x,x); printf("&y=%u:y=%d¥n", &y,y); return 0; #include <stdio.h> void swap(int *px, int *py) { int temp; temp=*px; *px=*py; *py=temp; printf("px=%u:*px=%d\n", px,*px); printf("py=%u:*py=%d\n", py,*py); } int main(void) int x=5, y=3; swap(&x, &y); printf("&x=%u:x=%d¥n", &x,x); printf("&y=%u:y=%d¥n", &y,y); return 0; 値呼び出し 参照呼び出し 3
値呼び出しと参照呼び出し Z:\nyumon2>swap_b &x=1245024:x=3 &y=1245028:y=5 Z:\nyumon2>swap_c px=1245032:*px=3 py=1245036:*py=5 &x=1245032:x=3 &y=1245036:y=5 swap 1245024 x 1245028 y 3 5 値の交換 5 3 値をコピー main 1245032 x 1245036 y 5 3 元のまま swap 1245024 px 1245028 py 1245032 1245036 アドレスをコピー → ポインタ main 1245032 x 1245036 y 5 3 ポインタによる 値の交換 3 5
swapのいろいろ #include <stdio.h> void swap2(int *px, int *py) { *px = *py - *px; *py -= *px; *px += *py; } int main(void) int x = 5, y = 3; swap2(&x, &y); printf("x = %d, y = %d\n", x, y); return 0; 変数 temp を使わない方法 *px += *py; *py = *px - *py; *px -= *py; または ポインタの交換はダメ 実数の場合、情報落ちが起きる swap2(&x, &x) はダメ
swapのいろいろ #include <stdio.h> void swap3(int *px, int *py) { *px ^= *py; *py ^= *px; } int main(void) int x = 5, y = 3; swap3(&x, &y); printf("x = %d, y = %d\n", x, y); return 0; ビット演算子が使えるのは、 32 bit OSの場合、4バイト (32ビット)以下の整数型のみ swap3(&x, &x) はダメ
プログラム例 11.5.1(数値積分) 定積分 x2 + y2 = 1 1 circle 0 1 y = x2 1 parabola 0 1
数値積分(台形公式) y y = f(x) f(a+2h) f(a+(n-1)h) f(a+h) f(b) f(a) h h h x O a a+h a+2h a+(n-1)h b h h h x y y = f(x)
プログラム例 11.5.1(数値積分) 台形公式 integral sum ← 0 h ← (upper - lower) / n sum ← sum + f(lower + i*h) 0.5*h*(2*sum + f(lower) + f(upper)) を戻す
積分ルーチンの仕様 関数名を内部で指定する場合 関数名を呼び出す側で指定する場合 関数1 積分ルーチン1 メイン関数 関数2 積分ルーチン2 専用ルーチン 積分ルーチン1 関数1 メイン関数 積分ルーチン2 関数2 関数3 積分ルーチン3 被積分関数ごとに作成 関数名を呼び出す側で指定する場合 汎用ルーチン 関数1 関数1 メイン関数 積分ルーチン 関数2 関数2 関数3 関数3 一つ作成すればOK
11.5 関数引数 関数名を渡すのに、関数へのポインタを使う プログラム例 11.5.1 より これまでに習ってきた例:実数 x,y が引数 double integral(double lower, double upper, int n, double (*f)(double)) { double h, sum; int i; sum = 0.0; ・ } 変数なら double f ポインタなら double *f となるところ… これまでに習ってきた例:実数 x,y が引数 単なるポインタと 区別するため、 関数へのポインタは カッコでくくる。 プログラム例 10.1.3 より double sum(double x, double y) { double z; z = x + y; return z; }
11.5 関数引数 プログラム例 11.5.1 #include <stdio.h> #include <math.h> double circle(double); double parabola(double); double integral(double, double, int, double (*f)(double)); int main(void) { printf("circle__:%u, %f¥n",circle,integral(0.0,1.0,100,circle)); printf("parabola:%u, %f¥n",parabola,integral(0.0,1.0,100,parabola)); return 0; } double型関数へのポインタ を意味する 円 放物線 積分 1行目では 関数名 circle を, 2行目では 関数名 parabola を 実引数としている 次のスライドに続く 関数名もポインタ
プログラム例 11.5.1 関数 circle、関数 parabola と 関数 integral の関数定義部 f は仮引数 double integral(double lower, double upper, int n, double (*f)(double)) { double h, sum; int i; printf("f:%u, ",f); sum = 0.0; h = (upper - lower) / n; for (i = 1; i < n; i++) sum += f(lower + i * h); return 0.5 * h * (2.0 * sum + f(lower) + f(upper)); } double circle(double x) return sqrt(1.0 - x * x ); double parabola(double x) return x * x; f は仮引数 f(…)の部分では、main で呼ばれたときに 実引数となっている関数を呼び出す integral(…, circle) なら関数 circle を呼び、 integral(…, parabola) なら関数 parabola を呼ぶ
実行例 コマンドラインオプションで分割数 n を入力できるようにし、 小数点以下15桁表示するように改良せよ。 Z:\nyumon2>ex11_5_1 f:4198736, circle__:4198736, 0.785104 f:4198768, palabora:4198768, 0.333350 関数へのポインタ(アドレス)が同じ コマンドラインオプションで分割数 n を入力できるようにし、 小数点以下15桁表示するように改良せよ。 Z:\nyumon2>ex11_5_1a 100000000 f:4198736, circle__:4198736, 0.785398163396719 f:4198768, palabola:4198768, 0.333333333333223
本日のパズル 次のプログラムは何を出力するか #include <stdio.h> #define PRINT(x,y) printf("%g\t%g\n",(double)x,(double)y) main() { double d=3.2, x; int i=2, y; x = (y=d/i)*2; PRINT(x,y); y = (x=d/i)*2; PRINT(x,y); x = d * (y =((int)2.9+1.1)/d); PRINT(x,y); } 1 2 3