1 関数の作り方

1.1 三角形の面積の計算

1.1.1 プログラム作成方法

ヘロン公式

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

を使って三角形の面積$ S$を計算するプログラムを作成する.ここで, $ (a,\,b,\,c)$は三 角形の三辺の長さである.三角形の面積を計算するためには,この三辺の長さの情報で必 要十分である.ここで,スモールエス$ s$が負になると,「三角形の一辺は,他の二辺の 和より短く,他の二辺の差よりも長い」という条件を満たさなくなり三角形が成立しない --ことにも注意してプログラムを作成しなくてはならない.

それでは,面積を計算する関数を考えよう.次のような関数が妥当だろう. -4pt

リスト1に面積を計算する関数を使った例を示す.ここで,三角形ができない場合の メイン関数の処理について注意が必要である.行目のif(menseki < -990)としてい ることを説明する.普通に考えれば,if(menseki == -999)としたいところではある が,これは絶対にダメである.変数mensekiの型は倍精度実数であるた め,丁度-999.00000000000000となるとは限らないからである.倍精度実数には必ず 誤差があるので,-999.00000000000001のように誤差がある可能性がある.このよう な理由から,倍精度実数を等価演算子==で比較することは厳に慎むべきである.こ れは分かりにくいバグの原因となる.一方,整数の場合は,誤差がないので等価演算子で の比較で問題を起こすことはない.
   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   if(test <= 0){
  43     S = -999.0;
  44   }else{
  45     S = sqrt(test);
  46   }
  47 
  48   return S;
  49 }



\fbox{実行結果}

辺1の長さ?      5.6
辺2の長さ?      8.3
辺3の長さ?      6.8
面積は,18.915076です.

1.1.2 実行の流れと変数

  1. プログラムの実行の準備2.すべて変 数はローカルの自動変数なので,この時点で変数は無い.
  2. メイン関数の実行の開始.メイン関数内にあるローカルの自動変数が生成される.生成される変数は初期化されていないので,値は不定である.
    メイン関数   hen1:?   hen2:?   hen3:?   menseki:?
    

  3. キーボードからデータの読み取り完了(19行目実行直後).
    メイン関数   hen1:5.6   hen2:8.3   hen3:6.8   menseki:?
    

  4. 関数helonの呼び出し(21行目実行直後).このとき関数helonの変数が作成される.そして,実引数の値が仮引数にコピーされる.
    メイン関数   hen1:5.6   hen2:8.3   hen3:6.8   menseki:?
    関数helon   a:5.6      b:8.3      c:6.8      s:10.35       S:?       test:?
    

  5. stestの計算(41行目実行直後).
    メイン関数   hen1:5.6   hen2:8.3   hen3:6.8   menseki:?
    関数helon   a:5.6      b:8.3      c:6.8      s:10.35       S:?       test:357.78
    

  6. 面積Sの計算(45行目実行直後).
    メイン関数   hen1:5.6   hen2:8.3   hen3:6.8   menseki:?
    関数helon   a:5.6      b:8.3      c:6.8      s:10.35       S:18.91   test:357.78
    

  7. 関数helonでの処理が終了(48行目実行)し,制御がメイン関数に戻る(21行目). 関数helonで定義した変数は全て自動変数なので,関数helonでの処理 が終了するとそれらは消滅する.45行目のreturn S;で返される値は,21行 目の関数helon(hen1, hen2, hen3)の値となり,面積を表す変数 mensekiに代入される.
    メイン関数   hen1:5.6   hen2:8.3   hen3:6.8   menseki:18.91
    

  8. 面積の表示(26行目実行直後).
    メイン関数   hen1:5.6   hen2:8.3   hen3:6.8   menseki:18.91
    

  9. プログラムの完了(29行目実行直後).メイン関数の変数は全て消滅する.return 0;でこのプログラムの呼び出し元に戻り値として,0を返す.このプログ ラムの呼び出し元は,LinuxというOSである.

1.2 三角形の面積と角度の計算

三角形の面積のみならず周長も計算する関数を実装したい場合,先ほどの方法だと困った ことが生じる.複数の戻り値--面積と周長--の値を同時に返せないのである.もちろん, 実引数や仮引数を使うこともできない.これらの引数は,呼び出し元から関数にデータを 渡すことしかできない.つまり, -4pt である.どうするか?.

いろいろな方法があるが,諸君のこれまでの知識ではグローバル変数を使う方法が良いだ ろう.リスト2に示すように計算結果をグローバル変数に格納する. こうすれば,複数のデータを呼び出し側へ返すことができる.

賢い諸君は,引数もグローバル変数で渡すことができることに気が付くだろう.もちろん, これも可能であるが,よくないプログラム作法である.グローバル変数は,いかなる関数 からもアクセス可能であるため,思わぬデータが紛れ込む可能性がある.このようなバグ は非常に分かりにくいので,原因を突き止めるのに大変な量力を要する.したがって,グ ローバル変数はできるだけ使わないようにしなくてはならない.

   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 }

1.2.1 グローバル変数の生成と消滅

リスト2のローカル変数はすべて自動変数なので,その挙動はリス ト2と同じである.説明するまでも無いだろう.グローバル変数Stotal_lenについて,その生成と消滅のタイミングについて述べておく.
ホームページ: Yamamoto's laboratory
著者: 山本昌志
Yamamoto Masashi
平成18年12月1日


no counter