PowerPoint プレゼンテーション

第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