第11章 関数について 11.1 標準ライブラリ関数 11.2 関数呼び出しのオーバーヘッド 11.3 大域変数 11.4 プロトタイプ宣言 11.5 関数引数 11.1 標準ライブラリ関数 予め定義されており、ユーザが定義・作成しなく ても使える関数 ヘッダ部に以下のマクロが必要 #include <stdio.h> ← printf, scanf等の入出力関数 #include <math.h> ← sqrt, sin等の数学関数 11.2 関数呼び出しのオーバーヘッド 関数を呼び出す時、実際の計算以外のことに 使われるマシンへの余分な負荷をオーバー ヘッドという 関数に渡されるデータのコピーを作るための 実行時間や、そのためのメモリ消費など → データ数が大量になると無視できなくなる 関数の呼び出し回数が少なくて済むプログラムに プログラム例11.2.2 #include <stdio.h> double sum(double, double); プロトタイプ宣言はこれでOK int main(void) { int count = 5; double a[] = {1.23, 2.34, 3.45, 4.56, 12.34}; double b[] = {4.56, 5.67, 6.78, 7.89, 19.2}; double c[50]; 関数 sum をデータの数だけ呼出している。 int i; (その度にオーバーヘッドの時間がかかる for (i = 0; i < count; i++) { ので、データが大量になるとバカにならない) c[i] = sum(a[i], b[i]); printf("%6.2f %6.2f %6.2f\n", a[i], b[i], c[i]); } return 0; } double sum(double x, double y) { return (x + y); } プログラム例11.2.3 #include <stdio.h> void sum(int, double *, double *, double *); int main(void) プロトタイプ宣言はこれでOK { int count = 5; double a[] = {1.23, 2.34, 3.45, 4.56, 12.34}; double b[] = {4.56, 5.67, 6.78, 7.89, 19.2}; double c[50]; a と&a[0]は同じもの int i; 関数sumの呼び出しは1回 sum(count, a, b, c); for (i = 0; i < count; i++) printf("%6.2f %6.2f %6.2f\n", a[i], b[i], c[i]); return 0; } void sum(int n, double *xp, double *yp, double *zp) { int i; for (i = 0; i < n; i++) { ポインタの中味*zpは変化するが、 *zp = *xp + *yp; zp++; xp++; yp++; 引数(ポインタ)自体は戻されない } } ふつうは zp[i] = xp[i] + yp[i] → 第12章 グローバル変数 11.3 大域変数(global variable) 全ての関数からアクセス可能な変数 10.4で学習した局所変数(local variable)とは 対立する概念 → 以下の3つのプログラム例を参照 10.4 関数と変数の可視範囲 より引用 関数内で宣言した変数は、その関数内でのみ可視 アクセス可能 変数の可視範囲 ⇒ スコープ という ある関数の中でのみ通用する変数 ⇒ 局所変数 という 局所変数の例―プログラム例 10.4.1― mainとscope_ruleの両方で同じ変数a,bを用いても良い int main(void) この a,b のスコープは main のみ { int a = 10, b = 30; printf(“関数呼び出し前 a = %d b = %d¥n”, a, b); scope_rule(); printf(“関数呼び出し後 a = %d b = %d¥n”, a, b); return 0; } 同じ変数名 a,b でも、関数毎に 別々に変数領域が割り当てられる void scope_rule(void) { int a, b; この a,b のスコープは scope_rule のみ a = 200; b = 400; printf(“関数内部では a = %d b = %d¥n”, a, b); } 大域変数の例―プログラム例 11.3.1― #include <stdio.h> void sum(void); void difference(void); double x, y; この x,y のスコープは全ての関数 int main(void) { x = 12.5; y = 56.7; sum(); difference(); return 0; } 関数 sum や difference で新たに変数宣言していない ので、x,y といえば、4行目で宣言された変数 x,y を指す void sum(void) { printf(“和は %f¥n”, x + y); 関数 sum や difference に引数は不要 } void difference(void) { printf(“差は %f¥n”, x - y); } 一見、便利! 混在する場合―プログラム例 11.3.2― #include <stdio.h> void scope_rule(void); int u, v; この u,v のスコープは全ての関数 int main(void) { u = 10; v = 20; printf(“関数の呼び出し前 %d %d¥n”, u, v); scope_rule(); printf(“関数の呼び出し後 %d %d¥n”, u, v); return 0; } 混乱を招くので 乱用しない 関数内での局所変数名に大域変数名と同じ void scope_rule(void) { ものを用いても構わない。 int u, v; この u,v のスコープは scope_rule のみ u = 100; v = 200; printf(“関数内では %d %d¥n”, u, v); } 大域変数を使わないプログラミングを心がけよう! 11.4 プロトタイプ宣言 プロトタイプ宣言とは?(教科書p.78参照) プログラム例 10.1.1 #include <stdio.h> double sum(double x, double y); int main(void) { ・ ・ ・ } double sum(double x, double y) { double z; z = x + y; return z; } プロトタイプ宣言 sum という関数を使う その詳細は後で定義 引数は double 型2個 戻り値は double 型 1行でこれだけの意味を持つ sum という関数の定義部 プログラム例 11.4.2 #include <stdio.h> int foo(int, int, double); double bar(double, double); int main(void) { ...; return 0; } int foo(int u, int v, double w) { ...; } double bar(double m, double n) { ...; } プロトタイプ宣言では、 関数の引数となる変数名は 省略できる 当然だが、関数定義では 仮引数は必要 11.5 関数引数 関数の引数として、関数へのポインタも使える プログラム例 11.5.1 より double integral(double lower, double upper, int n, double (*f)(double)) { double h, sum; 変数なら double f int i; ポインタなら double *f sum = 0.0; となるところ… ・ ・ ・ } これまでに習ってきた例:実数 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> double型関数へのポインタが #include <math.h> 引数であることを意味する double circle(double); double parabola(double); double integral(double, double, int, double (*f)(double)); int main(void) { printf("%f¥n", integral(0.0, 1.0, 100, circle)); printf("%f¥n", integral(0.0, 1.0, 100, parabola)); return 0; } 1行目では関数 circle を, 次のスライドに続く 2行目では関数 parabola を 実引数としている プログラム例 11.5.1 関数 circle、関数 parabola と 関数 integral の関数定義部 double integral(double lower, double upper, int n, double (*f)(double)) { double h, sum; int i; 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)); } f(…)の部分では、main で呼ばれたときに double circle(double x) 実引数となっている関数を呼び出す { return sqrt(1.0 - x * x ); } integral(…, circle) なら関数 circle を呼び、 integral(…, parabola) なら関数 parabola を呼ぶ double parabola(double x) { return x * x; } スキルアップタイム 1 以下はプログラム例11.2.2の改変。何を変えたか考えながら実行してみよう #include <stdio.h> double sum(double x, double y); int k = 0; int main(void) { int i, count = 8; double a[] = {1.23, 2.34, 3.45, 4.56, 5.67, 6.78, 7.89, 8.91}; double b[] = {2.34, 3.45, 4.56, 5.67, 6.78, 7.89, 8.91, 1.23}; double c[8]; for (i = 0; i < count; i++) { c[i] = sum(a[i], b[i]); printf("%7.3f %7.3f %7.3f %3d\n", a[i], b[i], c[i], k); } return 0; } double sum(double x, double y) { k++; return (x + y); } スキルアップタイム 2 以下はプログラム例11.2.3の改変。何を変えたか考えながら実行してみよう #include <stdio.h> void sum(int n, double *x, double *y, double *z); int k = 0; int main(void) { int i, count = 8; double a[] = {1.23, 2.34, 3.45, 4.56, 5.67, 6.78, 7.89, 8.91}; double b[] = {2.34, 3.45, 4.56, 5.67, 6.78, 7.89, 8.91, 1.23}; double c[8]; sum(count, a, b, c); for (i = 0; i < count; i++) printf("%7.3f %7.3f %7.3f %3d\n", a[i], b[i], c[i], k); return 0; } void sum(int n, double *xp, double *yp, double *zp) { int i; k++; for (i = 0; i < n; i++) { *zp = *xp + *yp; zp++; xp++; yp++; } } スキルアップタイム 3 プログラム例11.5.1を、何を求めているのか良く考えながら実行してみよう Integral 1 1 x dx 0 2 4 1 0.785398 1 3 x 1 2 x dx 0 3 0 3 1 circle 0 1 1 parabola 台形公式 n 1 h I f (a) f (b) 2 f (a i h) 2 i 1 0 1
© Copyright 2025 ExpyDoc