3 関数

3.1 関数の役割と作成方法

関数とは,処理の手続きをまとめたものである.関数を使うことにより,C言語のプログ ラムは -4pt とすることができる.特に,2番目が重要で,「プログラムのソースは分かりやすくなくてはならない」 ということが実現できる.

関数をつくり,使うためには,ソースプログラムを図4の ように記述する.必要な記述は,以下の3つである.

-4pt

図 4: ユーザー定義関数を含んだソースプログラムの書き方
\includegraphics[keepaspectratio, scale=1.0]{figure/how_to_make_function.eps}

3.2 データの受渡し

呼出元から関数へ処理に必要なデータは,引数として渡す.呼出元の引数を実引数,呼び 出された関数側の引数を仮引数(図5)と言う.関数を呼び出したとき, 実引数の値が仮引数の変数にコピーされる.それを使って,関数は処理を行う.一方,関 数が処理したデータを呼出元へ返すときには,戻り値をつかう.これは,return文 により返すことができる.

これらの引数と戻り値により,呼出元と関数の間でのデータの受渡しを行う.受渡しは, 一方通行であることを忘れてはならない. -4pt

必要な引数を伴って関数名を書けばその関数のプログラムを実行することができる.戻り 値で返された値は,次のように変数に格納する.

y=f(x);

これで,変数xの値を独立変数として $ -x^2 + 10x + 8 + 10\sin^2 x$の計算を行い, その結果を変数yに格納する.注意:呼出元のxが実引数.
図: 数学関数 $ f(x)=-x^2 + 10x + 8 + 10\sin^2 x$を計算するC言語のユーザー 定義関数.この関数はp.[*]のリスト1で使っている.
\includegraphics[keepaspectratio, scale=1.0]{figure/function.eps}

3.3 変数のスコープ

変数の有効範囲のことを変数のスコープと言う.それは宣言する場所で決まる.変 数のスコープは3種類あり,それぞれについて以下の箇条書きと図6に 示す. スコープが異なれば,同じ名前の変数名を使うことができる.同じ名前がある場合,優先 度の高い変数が使われる.優先度の順序は,ブロック内宣言の変数 $ \rightarrow$ローカ ル変数 $ \rightarrow$グローバル変数となる.スコープが狭い程,優先順位が高い.
図 6: 変数のスコープ(有効範囲)
\includegraphics[keepaspectratio, scale=0.8]{figure/scope.eps}

3.4 記憶クラス

メモリーにデータを格納する方法を示したものがC記憶クラスである.C言語には,4個の記憶 クラス指定子(1)auto,(2)extern,(3)register, (4)staticがある.これらは,変数宣言の先頭に
auto int hogehoge;
extern int fugafuga;
register int foo;
static int bar;

と書く.

記憶クラスは4種類あるが,この中でstaticはローカル変数3とグローバル変数では, 動作が異なる.したがって,5種類の動作があると考える.

記憶クラスを指定しない場合,自動変数(auto)となる.デフォルトは自動変数.そ のため,通常,自動変数を使う場合,記憶クラス指定子autoは書かない. 

表 1: 記憶クラスの特徴
記憶クラス 指定子 特徴
自動変数 auto ローカル変数の場合は,関数が呼び出される毎に記 憶領域の確保と初期化(初期値の指定がある場合)が行われ,関数の処理が終わったときに その変数は消滅する.グローバル変数の場合は,プログラム実行中,変数の記憶領域は保持される.
ローカル変数の静的変数 static プログラムの実行に先立って,記憶領域の確保と 初期が行われる.プログラムの実行中,記憶領域は保持される.
グローバル変数の静的変数 static 分割コンパイルの場合,そのファイルの みで使えるグローバル変数ということを示している.
外部変数 extern この記憶クラスはグローバル変数のみに使う.これが 指定されると,記憶領域の確保は行われず,どこか他のファイルにこの変数があること を示す.記憶領域は,どこか他のファイルにある変数を使う.
レジスター変数 register レジスターを記憶領域として使う.レジスター はCPUの記憶領域で,メインメモリーよりも高速のアクセスが可能である.したがって, 処理の高速になる.

3.5 関数の例

3.5.1 関数を使った基本プログラム

関数

  $\displaystyle f(x)=-x^2 + 10x + 8 + 10\sin^2 x$   $\displaystyle -10\leqq x \leqq 10$   (1)

の最大値を求めるプログラムである.これは,10月20日配布のプリントに書かれている例.
   1 #include <stdio.h>
   2 #include <math.h>
   3 
   4 double f(double x);           //プロトタイプ宣言
   5 
   6 //========================================================
   7 // メイン関数
   8 //========================================================
   9 int main(void){
  10   double x, dx, xmin, xmax, y;
  11   double max_y, max_x;
  12   int i, ncal;
  13 
  14   //--- 計算条件設定 ---
  15   xmax = 10;
  16   xmin = -10;
  17   dx = 0.0001;
  18   ncal = (xmax-xmin)/dx;
  19 
  20   //--- 暫定最大値 ---
  21   max_x = xmin;
  22   max_y = f(xmin);
  23 
  24   //--- 最大値検索 ---
  25   for(i=1; i<=ncal; i++){
  26     x = xmin + i*dx;
  27     y = f(x);
  28     if(max_y <= y){        //最大値が見つかった場合
  29       max_x = x;
  30       max_y = y;
  31     }
  32   }
  33 
  34   printf("%fのとき,最大%fとなる.\n",max_x, max_y);
  35 
  36   return 0;
  37 
  38 }
  39 
  40 //============================================================
  41 // ユーザー定義関数
  42 //============================================================
  43 double f(double x){
  44   double y;
  45 
  46   y = -x*x + 10*x + 8 + 10*sin(x)*sin(x);        // 関数の計算
  47 
  48   return y;
  49 }

3.5.2 戻り値の工夫

リスト2は,ヘロン公式

$\displaystyle s$ $\displaystyle =\frac{a+b+c}{2}$ (2)
$\displaystyle S$ $\displaystyle =\sqrt{s(s-a)(s-b)(s-c)}$ (3)

を使って三角形の面積$ S$を計算するプログラムである.これは,12月1日配布のプリン トに書かれている例.

もし,仮引数で渡された三辺では三角形ができなかった場合,戻り値を-999 とする. 三角形ができた場合の面積は必ず正である.もし戻り値が負の値となった場合,呼び出し 元は渡した実引数が不適で,三角形が成立しなかったことが分かる.-999のようなヘンテ コリンな数字を戻り値とするのはプログラミングの定石である.そして,倍精度実数の比 較には等価演算子(==)を使わないこと.


   1 #include <stdio.h>
   2 #include <math.h>
   3 
   4 double helon(double a, double b, double c);  //プロトタイプ宣言
   5 
   6 //===========================================================
   7 // メイン関数
   8 //===========================================================
   9 int main(void)
  10 {
  11   double hen1, hen2, hen3;
  12   double menseki;
  13 
  14   printf("辺1の長さ?\t");
  15   scanf("%lf",&hen1);
  16   printf("辺2の長さ?\t");
  17   scanf("%lf",&hen2);
  18   printf("辺3の長さ?\t");
  19   scanf("%lf",&hen3);
  20 
  21   menseki = helon(hen1, hen2, hen3);
  22 
  23   if(menseki < -990){
  24     printf("入力した辺では,三角形はできません!!!!!\n");
  25   }else{
  26     printf("面積は,%fです.\n", menseki);
  27   }
  28 
  29   return 0;
  30 }
  31 
  32 //===========================================================
  33 // ユーザー定義関数
  34 //===========================================================
  35 double helon(double a, double b, double c)
  36 {
  37   double s, S, test;
  38 
  39   s=(a+b+c)/2;
  40   test=s*(s-a)*(s-b)*(s-c);
  41 
  42   printf("%f\t%f\t%f\n",a,b,c);
  43   if(test <= 0){
  44     S = -999.0;
  45   }else{
  46     S = sqrt(test);
  47   }
  48 
  49   return S;
  50 }

3.5.3 複数の戻り値

三角形の面積と周長のように2つの値を計算する関数の場合,戻り値でその値を返すこと ができない.引数を使って,値を返すこともできない. -4pt

このような場合,グローバル変数を使って,値を返す4

   1 #include <stdio.h>
   2 #include <math.h>
   3 
   4 void info_tri(double a, double b, double c);    // プロトタイプ宣言
   5 double S, total_len;                              // グローバル変数
   6 
   7 //===========================================================
   8 // メイン関数
   9 //===========================================================
  10 int main(void)
  11 {
  12   double hen1, hen2, hen3;
  13   double menseki;
  14 
  15   printf("辺1の長さ?\t");
  16   scanf("%lf",&hen1);
  17   printf("辺2の長さ?\t");
  18   scanf("%lf",&hen2);
  19   printf("辺3の長さ?\t");
  20   scanf("%lf",&hen3);
  21 
  22   info_tri(hen1, hen2, hen3);
  23 
  24   if(S < -990){
  25     printf("入力した辺では,三角形はできません!!!!!\n");
  26   }else{
  27     printf("面積は,%fです.\n", S);
  28     printf("周長は,%fです.\n", total_len);
  29   }
  30 
  31   return 0;
  32 }
  33 
  34 //===========================================================
  35 // ユーザー定義関数
  36 //===========================================================
  37 void info_tri(double a, double b, double c)
  38 {
  39   double s, test;
  40 
  41   s=(a+b+c)/2;
  42   test=s*(s-a)*(s-b)*(s-c);
  43 
  44   if(test<=0){
  45     S = -999.0;
  46   }else{
  47     S = sqrt(test);
  48     total_len = a+b+c;
  49   }
  50 
  51 }

3.5.4 void型を使った関数

引数や戻り値の無い関数を宣言するときには,voidと型宣言をする.
   1 #include <stdio.h>
   2 
   3 void write_file(void);                       // プロトタイプ宣言
   4 
   5 //============================================================================
   6 // メイン関数
   7 //============================================================================
   8 int main(void){
   9 
  10   write_file();                              // 関数の呼出
  11 
  12   return 0;
  13 }
  14 
  15 //============================================================================
  16 // void型の関数
  17 //============================================================================
  18 void write_file(void){
  19   FILE *fuga;
  20 
  21   fuga = fopen("hello.txt", "w");            // ファイルのオープン
  22   
  23   fprintf(fuga, "Hello World !!!\n");        // ファイルへの書き出し
  24 
  25   fclose(fuga);                              // ファイルのクローズ
  26 }

3.5.5 ファイルへの書き込み

先週は,さまざまなユーザー定義関数を学習した.その内容は,以下のようなものであっ た.
   1 #include <stdio.h>
   2 
   3 double hogehoge(double x);                        // プロトタイプ宣言
   4 
   5 //============================================================================
   6 // メイン関数
   7 //============================================================================
   8 int main(void){
   9 
  10   FILE *out;                                      // ファイルの情報を格納
  11   int i, np;
  12   double x, dx, xmax, xmin;
  13 
  14   printf("xの最小値\t");                           // キーボード入力
  15   scanf("%lf", &xmin);
  16   printf("xの最大値\t");
  17   scanf("%lf", &xmax);
  18   printf("プロット点の数\t");
  19   scanf("%d", &np);
  20 
  21   dx = (xmax - xmin)/np;                          //データ間隔計算
  22 
  23   out=fopen("data.txt","w");                      //ファイルをオープン
  24 
  25   for(i=0; i<=np; i++){
  26     x = xmin + i*dx;
  27     fprintf(out, "%f\t%f\n", x, hogehoge(x));     //データの書き込み
  28   }
  29 
  30   fclose(out);                                    //ファイルをクローズ
  31 
  32   return 0;
  33 }
  34 
  35 //============================================================================
  36 //  プロットする数学関数
  37 //============================================================================
  38 double hogehoge(double x){
  39   double y;
  40 
  41   y=(3*x+2)/(x+2);
  42 
  43   return y;
  44 }


ホームページ: Yamamoto's laboratory
著者: 山本昌志
Yamamoto Masashi
平成18年12月1日


no counter