- 関数とは
#include <math.h>
.....
y = sin (1.5); ← 関数(ライブラリ関数)
.....
C言語:「プログラムとは関数の集まりである」→「利用者は関数を作成する必要がある」
- 関数を利用するメリット
- プログラムが機能毎に整理される。
- 分かりやすく、デバッグや改良が簡単な構造になる。
- 異なったプログラムの中で利用することが可能。
- 関数定義
関数値のデータ型名 関数名(引数1のデータ型 引数1, ..., 引数nのデータ型 引数n)
{
関数内で用いるデータの宣言部分
関数の本体
リターン文(戻り値)
}
- 実際の関数例
- 単純な和の計算(整数型)
int add(int x, int y)
{
int z;
z = x + y;
return(z);
}
省略して書くと
add(int x, int y)
{
return(x+y);
}
- 割算の計算(倍精度型)
double devide(double a, double b)
{
return(a/b);
}
- 単純な表示(void型;戻り値なし)
void printout(char s)
{
printf("%s", s);
}
- 関数の呼出
add(int x, int y)
{
return (x +y);
}
main()
{
int a, b, c;
a = 1; b = 2;
c = add(a, b);
printf("%d¥n", c);
printf("5 +3 = %d¥n", add(5, 3));
}
- ローカル変数とグローバル変数(変数の有効範囲; scope)
- 関数内で宣言した変数はその関数内でのみ有効。
- プログラム中で共通の変数を使いたいときは、グローバル変数を定義する。
- ローカル変数
double multiple(int a, int b)
{
int i, j, k; ←ローカル変数(main()中のi,j,kとは何ら関係ない)
....
}
main()
{
int i, j, k; ←ローカル変数(multiple()中のi,j,kとは何ら関係ない)
k = multiple(i, j);
}
- グローバル変数
#include <stdio.h>
int num; ←グローバル変数
double count(int a, int b)
{
int i, j, k; ←ローカル変数
...
num = a + b; ←ここで加えた変更は、main()中のnumにも有効
...
}
main()
{
int a, b; ←ローカル変数
...
num = ... ←ここで加えた変更は、count()中のnumにも有効
...
}
- 関数を作成する際の注意点
- 関数名として予約語(例えばintなど)は使えない。
- 関数宣言文の引数とmain中の変数の順序。
- 関数部分は呼出す前に書いておく。
- 練習問題1:結晶によるX線回折において、実際の回折強度にはこれまでに計算してきたF2に加えて、
回折角度に依存するローレンツ因子Lを考慮する必要がある。そのLは次式で与えられ、
L = (1+cos22θ)/(sin2θcosθ)
X線回折強度 I は、
I ∝ L × F2
である。前回作成したプログラムに、このローレンツ因子を計算する関数を加え、
X線回折強度が0.001以上の面を表示を得るプログラムに改良せよ。以下に計算結果を示す。
(実行例:入力部分は下線部分)
Lattice constant a, c (nm) = 0.3209 0.5211
Wavelength (nm) = 0.15405
Number of atoms in unit cell = 2
No. 0 (x, y, z, f) = 0 0 0 12
No. 1 (x, y, z, f) = 0.6667 0.3333 0.5 12
- 再帰的関数呼び出し(recursive call)
→関数の中でその関数自身を呼び出すこと
リスト8.2(p.62)を簡略化したこのプログラムを入力し、その動作について考えてみよ。

再帰的関数呼び出しを利用する際の注意事項
- 漸化式等をプログラムするのに非常に有用
- 実行速度の低下や記憶容量の増加を伴うので、上の有用さとのバランスを考慮
- 無限ループに注意
- 練習問題2:p.70の課題4に取り組んでみよ。解答例(fractorial2.c)
- 手続き(Procedure)
- 関数と同様な定義、利用方法
- ただし、幾つもの変数を処理し、その結果を返すことが可能
- 手続きの例
void calc(int a, int b, int *x, int *y)
{
*x = a + b;
*y = a - b;
}
main()
{
int a, b, x, y;
a = 1; b =2;
calc(a, b, &x, &y);
printf("%d %d¥n", x, y);
}
- 上の例の解説
- main中からcalcを呼ぶときにa, bは従来と同様
→a, bの中身(この場合1と2)をcalcに引き渡す。(値呼び;call by value)
- しかし、x, yの前には&がついている。
→x, yの「変数のアドレス(変数がメモリ上において格納されている番地)」を手続きcalcに引き渡す。(参照呼び;call by reference)
- この場合、x, yのアドレスが手続きcalcに引き渡されるので、それを受けるcalcでのx, yの変数宣言はポインタ型。
- 関数calcの中で、x, yの値を参照するには、*x, *yを用いる。
- 配列を手続きおよび関数に引き渡す方法
- 配列名(例えばa[100]のa)は一連の配列が格納されているメモリ領域の先頭アドレスを有している。
→「配列名」はポインタ変数。詳しくは、配列とポインタ(第6回)を参照のこと。
例1)
#include <stdio.h>
void reverse(int *a) /* int a[], int a[10]も可 */
{
int b[10],i;
for(i=0; i<10; ++i) b[9-i] = a[i];
for(i=0; i<10; ++i) a[i] = b[i];
}
main()
{
int a[10]={0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, i;
reverse(a);
for(i=0; i<10; ++i) printf("%d\n", a[i]);
}