プログラミングおよび演習 NO.4
Last-Modified: 2015.05.25

何のために制御文を使うか?

プログラムは、普段は上の行から下の行に向かって1行づつ順番に実行していきます。制御文は、条件式に応じてプログラムの本来の順番を変えて実行させたい場合に 使います。 制御文には、

 @ 選択処理(条件に応じて処理を選択する)

 A 反復処理(条件に応じて処理を何回も繰り返す)


があります。 @の代表的な制御文としてif文やswitch文があります。
今日は、その中で比較的出番の多いif文について勉強します。

if文

if文は

 if(条件式){

   複数の実行文1;

 }else{

   複数の実行文2;

 }

の形をしています。この場合、条件式が成立する時に文1を実行し、不成立の時に文2を実行することになります。ここで、条件式は、以下のように約束します。

条件式   意 味 注 意
比較演算子  
 x==a  xがaに等しい?x=aと間違えない
 x!=a xがaに等しくない? 
 x>a xがaより大きい? 
 x<a xがaより小さい? 
 x>=a xがa以上? 
 x<=a xがa以下? 
論理演算子  
(式1)&&(式2)(式1)と(式2)が同時に真? 
(式1)||(式2)(式1)と(式2)のどちらかが真? 
!(式1)(式1)が偽? 


if文は上記のスタイル以外に、


if(条件式){

  文1;


のように、else文がない書き方もあります。反対に、

if(条件式1){

  複数の実行文1;

 }else if(条件式2){

  複数の実行文2;

 }else if(条件式3){
 
  複数の実行文3;

 }else {

  複数の実行文4;
}

のように書くと、(条件式1)が成立するときは、実行文1を実行、(条件式2)が成立するときは実行文2を実行、‥‥、何れも成立しない場合は、実行文4を実行します。

なお、{ }の中の実行文が1つだけ(単文)の場合は、

  if(条件式)

    文1;

のように、{ }を省略して書くことができます。


以下のプログラムは、キーボードから読み込んだ正の整数値の大きさを判断してメッセージを出力するプログラムです。
例4-1
#include <stdio.h>
int main(void)
{
    int x;

    printf("\n正の整数値を入れてください\n");
    fflush(0);
    scanf("%d",&x);
    printf("入力した値は%dです\n",x);

    if(x < 0 ){
       printf("負または大きすぎる値が入力されました。\n");
       printf("もう一度やり直してください。\n");
    }else if(x>100000000)
       printf("入力した値は一億より大きい数です\n");

       else if(x>1000000)
       printf("入力した値は百万より大きく一億以下の数です\n");

       else if(x>1000)
       printf("入力した値は千より大きく百万以下の数です\n");

       else 
       printf("入力した値は千以下の数です");

    return(0);
}


演習問題 4−1 (Revised : 2015/05/19)

自分の身長と実測体重をキーボードから入力すると、標準体重、BMI(Body Mass Index)および肥満度を計算して、

 入力された身長は   m、 実測体重は   kg。
 上記の場合の標準体重は   kg、 BMI指数は    、 肥満度は    %。


のように、計算結果を表示するプログラムを作りなさい。なお、それぞれの計算式は以下のとおり。

      体重(kg)
 BMI=              
     身長(m)×身長(m)

 標準体重(kg)=身長(m)×身長(m)×22

          (実測体重−標準体重)×100
 肥満度(%)=                   
               標準体重

次に、BMI指数を使って、

 BMI<18.5          低体重
 18.5≦BMI<25       標準
 25≦BMI<30        肥満度1
 30≦BMI<35        肥満度2
 35≦BMI<40        肥満度3
 40≦BMI          肥満度4


と判定して肥満度などについてのコメントを出力するように改良すること。さらに、BMIが25以上については以下のコメントも出力できるようにすること。

「高血圧症、高中性脂肪血症の発病率が2倍以上に増えます。注意してください。」

●補足説明
 ・以上の課題は一つのプログラムにまとめて下さい。
 ・実行結果は少なくとも3つの身長と体重の例について示してください。
 一つ目はBMIが40を超える場合、二つ目はBMIがちょうど25の場合、
 最後はそれ以外のBMIの値をとる好きな身長と体重の組み合わせを入力してください。

 ・考察は、それぞれの実行結果に対して別々に行ってください。特に、if文の働きに注意しながらプログラムの動作を説明してください。


算 術 関 数

図のような直角三角形の辺の長さを求める問題を考えてみます。ピタゴラスの定理を用いると、斜辺cは、辺a, bを用いて、

 c=√(a2+b2 )

から計算できます。また、辺cと角度θが分かっているとき、辺a,bの長さは、三角関数を使って

  a=c cosθ,b=c sinθ

から計算できます。このような身近なところで、平方根や三角関数を使う計算がよくでてきます。

関数電卓の名前で呼ばれている電卓では、平方根や三角関数の計算機能が備っています。これと同じように、C言語の場合も、よく使う算術関数はコンパイラの標準関数ライブラリで用意されていて、簡単に使えるようになっています。C言語が用意している算術関数の一覧を以下にあげておきます。

C言語が用意している算術関数一覧
関数名引数及び関数の型機能
abs(i)int整数iの絶対値を求める
acos(x)double実数xの逆余弦を求める
asin(x)double実数xの逆正弦を求める
atan(x)double実数xの逆正接を求める
ceil(x) double実数xの小数点以下を切り上げる
cos(x)double 実数xの余弦を求める
exp(x) double eのx乗を求める
fabs(x) double 実数xの絶対値を求める
floor(x) double 実数xの小数点以下を切り捨てる
fmod(x,y) double 実数xをyで割った剰余を求める
log(x) double 実数xの自然対数を求める
log10(x) double実数xの常用対数を求める
pow(x,y) double 実数xのy乗を求める
sin(x)double 実数xの正弦を求める
sqrt(x) double 実数xの平方根を求める
tan(x)double 実数xの正接を求める

■算術関数の基本的な使い方
関数を使うときには、まず、計算したい値を関数の( )の中の引数に入れます。平方根sqrt()関数を例にとると次のように書きます(これらは、数式どおりに書くのと殆ど同じです)。

  sqrt(x);    変数xの平方根
  sqrt(3.0);   数値定数 3.0 の平方根
  sqrt(2.0*(x+y)/z);   計算式 2.0*(x+y)/z の平方根

次に、こうして計算した平方根の結果(関数の計算結果)の取り出し方は次のようにします(これも数式を書くのと同じように使うことができます)。

  a=sqrt(x);   変数aに平方根の計算結果を代入
  a=x*y*sqrt(x)+b;   計算式の一部として用いる。計算結果を変数aに代入。

上記の計算結果は、

  printf("平方根=%e\n",a);

とすれば画面に表示できます。あるいは、printf()文の中に、

  printf("xの平方根は%e\n",sqrt(x));
  printf("計算式の平方根は%e\n",x*y*sqrt(x)+b)
;

のように直接書く事もできます。この場合は、変数aを使わないで済ませることができます。
以上で算術関数の基本的な使い方が分かったと思いますが、算術関数を使うときには次の点に注意する必要があります。
 
■算術関数を使うときの注意
1.プログラムの先頭で、

 #include <math.h>

のように、ヘッダーファイル<math.h>をインクルードすることを忘れないでください。(今まで、printf()関数やscanf()関数を使う際に、 <stdio.h> をインクルードしていたのもこれと同じです)

2.関数の()の中の引数、ならびに関数の計算結果は、決められた型に合わせるように注意してください。例えば、sqrt()関数の引数ならびに関数の型は、double型です。まず、引数の型を間違えて、

  sqrt(3)

としたり、i をint型の変数で宣言しているのに、 

  sqrt(i)

と書くのは本当は間違いです。しかし、コンパイラがdouble型に自動変換するため、これでもおおよそ正しく計算してくれます。ところが、それをあてにしてうっかり

  sqrt(7/2*2)

としてしまうと、意図に反して間違った計算をする場合があります(どこがまずいかわかりますか?)。このため、引数の型は厳密に合わせて書くことにしたほうが賢明です。
一方、関数値の型の不一致の例として、printf("xの平方根は%d\n",sqrt(x)); のように、printf()文の書式を間違えることがあります。言うまでもなく、この場合はまったくでたらめな結果が表示されてしまいます。

次の問題は、if文と算術関数の使い方に関する応用問題です。挑戦してみてください。

演習問題 4−2 (Revised : 2015/05/25)

1.以下の実行文を実行し、異なる結果が得られることを確認し、その理由について考察せよ。また、表示される数値はそれぞれ何の平方根であるかについて答えよ。

    printf("sqrt(7/2*2)=%le\n",sqrt(7/2*2));
    printf("sqrt(7.0/2.0*2.0)=%le\n",sqrt(7.0/2.0*2.0));


2.以下の二次式と一次式で表される曲線と直線の交点を計算せよ。

  y = sx2+tx+u
  y = vx+w

プログラムは、交点の有無を判定し、交点がある場合には交点の数と(x,y)座標を、存在しない場合にはその旨を伝える文章を表示すること。 次の5通りの(s t u v w)の場合について計算をし、その結果を手計算の結果と比較した上で考察すること。考察には必ず計算誤差の評価とif文の動作の説明を含めること。  

 (0 1 1 -1 0) (1 -2 1 0.2 -0.21) (-1 -2 2 -1 -1) (1 -2 2 -1 -1) (好きな数の 組合せ)

なお、実数係数a, b, cをもつ2次方程式、
  y = ax2+bx+c   

の判別式と解の公式は以下の通りである。a=0の場合や重根の場合などに注意をし、交点の有無の判定や交点数の座標の計算に問題が生じないようにプログラムを工夫すること。

2次方程式の解:
(i)a=0のとき、
    x= -c/b

(ii)a≠0のとき、 判別式 
   D=b2-4ac

を計算。

(ii)-1  D=0のとき(重根)、
   x=-b/2a

(ii)-2  D>0のとき(実根)、
     -b±√D
  x=       
       2a

(ii)-3  D<0のとき(複素根)、虚数単位 i を用いて
     -b± i √-D
  x=        
       2a

[注意] (i)、(ii)-1のときは1つの解だけを、(ii)-2のときは2つの実数の解を、(ii)-3のときは2つの複素数の解を、各々を場合分けして表示するようにすること。

(時間が余った人に)
(ii)-1の重根の条件判定は、if(D==0) とした場合、ほぼ同じ数が2つ出力されてしまい、期待どおりに判定してくれない。どのようにすれば、よいか考えること(ヒント:if( fabs(D)<1e-15 )のような条件判定文に変更する。なぜこの式を使うか、理由を含めて考察すること)。