人生最悪の忘れ物

◎1--------------------------------------------
#include<stdio.h>
#include<math.h>

double maxdt(double a,double b);
void disp_sqrt(double n);

int main(void)
{
double mx;

mx=maxdt(22.33,44.55);
printf("mx=%f\n",mx);

disp_sqrt(3.0);
disp_sqrt(-6.0);

return 0;
}

double maxdt(double a,double b)
{
if(a>b)
return a;
else
return b;
}

void disp_sqrt(double n)
{
if(n<=0.0) return;
printf("%f の平方根=%f\n",n,sqrt(n));
}
-----------------------------------------------
◎2-------------------------------------------
#include<stdio.h>
#include<math.h>

double maxdt(double a,double b);
void disp_sqrt(double n);

int main(void)
{
double mx;

mx=maxdt(22.33,44.55);
printf("mx=%f\n",mx);

disp_sqrt(3.0);
disp_sqrt(-6.0);

return 0;
}

double maxdt(double a,double b)
{
if(a>b)
return a;
else
return b;
}

void disp_sqrt(double n)
{
if(n<=0.0){
return;
printf("%f の平方根=%f\n",n,sqrt(n));
}
}
-----------------------------------------------
◎3--------------------------------------------
#include<stdio.h>
#include<math.h>

double maxdt(double a,double b);
void disp_sqrt(double n);

int main(void)
{
double mx;

mx=maxdt(22.33,44.55);
printf("mx=%f\n",mx);

disp_sqrt(3.0);
disp_sqrt(-6.0);

return 0;
}

double maxdt(double a,double b)
{
if(a>b)
return a;
else
return b;
}

void disp_sqrt(double n)
{
if(n<=0.0){
return;
}
else{
printf("%f の平方根=%f\n",n,sqrt(n));
}
}
--------------------------------------------------

◎1は参考書を参考に作ったものです。
◎1は正常に動きます。
以上3つのプログラムで、疑問に思ったのは、関数「void disp_sqrt(double n);」についてなのですが、自分はif文が文が1つでもカッコ{ }を付けたい考えなので、◎1の「void disp_sqrt(double n)」の関数のif文に{}を付けようと思い、まず◎2のように変えたところ、平方根の表示が何も出ませんでした。

return文も文の1つだと考え、◎3のような形は正常に動きました。
return文とprintf文の2つの文があるという考えは間違っているのでしょうか?
後、◎1は何故{ }が無くてもよく、◎2は何も表示されないのでしょうか?
教えていただけると嬉しいです。

A 回答 (6件)

「if文」の構文(書き方)は、以下の通りです。



if (評価式) 真の時に実行する文 [else 偽の時に実行する文]

「[」と「]」で括られた「else 偽の時に実行する文」は省略可能です。

上記の「真の時に実行する文」や「偽の時に実行する文」は、以下の2通りあります。

・パターンA
命令文;

・パターンB
{ 1つまたは2つ以上の命令文; }

従って、以下のようになります。

・パターン1
if (評価式) 命令文;

・パターン2
if (評価式) { 1つまたは2つ以上の命令文; }

・パターン3
if (評価式) 命令文; else 命令文;

・パターン4
if (評価式) 命令文; else { 1つまたは2つ以上の命令文; }

・パターン5
if (評価式) { 1つまたは2つ以上の命令文; } else 命令文;

・パターン6
if (評価式) { 1つまたは2つ以上の命令文; } else { 1つまたは2つ以上の命令文; }

まず、上記の6パターンを覚えて下さい。

>◎1
>if(n<=0.0) return;
>printf("%f の平方根=%f\n",n,sqrt(n));

この場合のif文はパターン1です。

「if(n<=0.0) return;」だけがif文です。printf関数の部分は、if文とは無関係です。

もし{}を付けるなら
if(n<=0.0) { return; }
printf("%f の平方根=%f\n",n,sqrt(n));
となります。

if文の条件が真でも偽でも、どっちの場合もprintf関数を通る筈です。

しかし「return文」は「それ以下はすべて無視して、呼び出し元に帰る文」ですから、return文を実行した時はprintf関数以降は実行しません。

逆に言うと「return文を実行しなかった時はprintf関数以降を実行する」と言う事です。

printf関数が呼ばれる条件は「return文を実行しない時」です。

「return文を実行しなかった時はprintf関数以降を実行する」のですから◎3の
>if(n<=0.0){
>return;
>}
>else{
>printf("%f の平方根=%f\n",n,sqrt(n));
>}
と同じ動作をします。

>◎2
>if(n<=0.0){
>return;
>printf("%f の平方根=%f\n",n,sqrt(n));
>}

この場合のif文はパターン2です。

printf関数を呼ぼうとしている所までがif文です。

printf関数が呼ばれる条件は「return文を実行する時」です。

しかし「return文」は「それ以下はすべて無視して、呼び出し元に帰る文」ですから、return文を実行した時はprintf関数以降は実行しません。

return文を実行した時はprintf関数は呼ばれません。呼び出し元に帰ってしまいますから。

return文を実行しない時もprintf関数は呼ばれません。条件が偽の時に実行する文がありませんからね。

つまり、どっちの条件でも、printf関数は呼ばれません。

賢いコンパイラであれば「どこからも実行されない文があるけど、大丈夫か?」と言う警告を出す筈です。

>◎3
>if(n<=0.0){
>return;
>}
>else{
>printf("%f の平方根=%f\n",n,sqrt(n));
>}

説明不要ですね。

以下、プログラムの流れを掴む為の、長~い説明。無視しても可。

elseが無いif文は「else {;}」が省略されていると考えられるので、◎1、◎2に省略された「else {;}」を補ってみます。

◎1
void disp_sqrt(double n)
{
if(n<=0.0) return; else {;}
printf("%f の平方根=%f\n",n,sqrt(n));
}

◎2
void disp_sqrt(double n)
{
if(n<=0.0){
return;
printf("%f の平方根=%f\n",n,sqrt(n));
} else {;}
}

◎3
void disp_sqrt(double n)
{
if(n<=0.0){
return;
}
else{
printf("%f の平方根=%f\n",n,sqrt(n));
}
}

次に「条件が真の時はelse文以降は無視し、条件が偽の時はelse文より前を無視する」のですから、if文のelse文をgoto文に置き換えしてみます。

◎1
void disp_sqrt(double n)
{
if(n<=0.0) { return; goto end_if; }
{;}
end_if:
printf("%f の平方根=%f\n",n,sqrt(n));
}

◎2
void disp_sqrt(double n)
{
if(n<=0.0){
return;
printf("%f の平方根=%f\n",n,sqrt(n));
goto end_if;
}
{;}
end_if:
}

◎3
void disp_sqrt(double n)
{
if(n<=0.0){
return;
goto end_if;
}
printf("%f の平方根=%f\n",n,sqrt(n));
end_if:
}

◎1、◎2、◎3のreturn文は「それ以下はすべて無視して、呼び出し元に帰る文」ですから、goto文に置き換えしてみます。

◎1
void disp_sqrt(double n)
{
if(n<=0.0) { goto end_func; goto end_if; }
{;}
end_if:
printf("%f の平方根=%f\n",n,sqrt(n));
end_func:
}

◎2
void disp_sqrt(double n)
{
if(n<=0.0){
goto end_func;
printf("%f の平方根=%f\n",n,sqrt(n));
goto end_if;
}
{;}
end_if:
end_func:
}

◎3
void disp_sqrt(double n)
{
if(n<=0.0){
goto end_func;
goto end_if;
}
printf("%f の平方根=%f\n",n,sqrt(n));
end_if:
end_func:
}

何もしない文の「{;}」を削り、行を整理してみます。

◎1
void disp_sqrt(double n)
{
if(n<=0.0) {
goto end_func;
goto end_if;
}
end_if:
printf("%f の平方根=%f\n",n,sqrt(n));
end_func:
}

◎2
void disp_sqrt(double n)
{
if(n<=0.0){
goto end_func;
printf("%f の平方根=%f\n",n,sqrt(n));
goto end_if;
}
end_if:
end_func:
}

◎3
void disp_sqrt(double n)
{
if(n<=0.0){
goto end_func;
goto end_if;
}
printf("%f の平方根=%f\n",n,sqrt(n));
end_if:
end_func:
}

goto文で飛んでしまった行の次の行は、無視されますから、削ります。

◎1
void disp_sqrt(double n)
{
if(n<=0.0) {
goto end_func;
}
end_if:
printf("%f の平方根=%f\n",n,sqrt(n));
end_func:
}

◎2
void disp_sqrt(double n)
{
if(n<=0.0){
goto end_func;
}
end_if:
end_func:
}

◎3
void disp_sqrt(double n)
{
if(n<=0.0){
goto end_func;
}
printf("%f の平方根=%f\n",n,sqrt(n));
end_if:
end_func:
}

goto文が削られラベルだけ残ってしまったラベル文を削ります。

◎1
void disp_sqrt(double n)
{
if(n<=0.0) {
goto end_func;
}
printf("%f の平方根=%f\n",n,sqrt(n));
end_func:
}

◎2
void disp_sqrt(double n)
{
if(n<=0.0){
goto end_func;
}
end_func:
}

◎3
void disp_sqrt(double n)
{
if(n<=0.0){
goto end_func;
}
printf("%f の平方根=%f\n",n,sqrt(n));
end_func:
}

{}の中に文が1つしかない所は、{}を削って、if文を1行にしてみます。

◎1
void disp_sqrt(double n)
{
if(n<=0.0) goto end_func;
printf("%f の平方根=%f\n",n,sqrt(n));
end_func:
}

◎2
void disp_sqrt(double n)
{
if(n<=0.0) goto end_func;
end_func:
}

◎3
void disp_sqrt(double n)
{
if(n<=0.0) goto end_func;
printf("%f の平方根=%f\n",n,sqrt(n));
end_func:
}

「すぐ真下にgotoする」のは「何もしない」と同じなので「{;}」に置き換えます。ついでにラベル文も削ります。

◎1
void disp_sqrt(double n)
{
if(n<=0.0) goto end_func;
printf("%f の平方根=%f\n",n,sqrt(n));
end_func:
}

◎2
void disp_sqrt(double n)
{
if(n<=0.0) {;}
}

◎3
void disp_sqrt(double n)
{
if(n<=0.0) goto end_func;
printf("%f の平方根=%f\n",n,sqrt(n));
end_func:
}

「else文が無く、真の時に何もしないif文」は「if文が無いのと同じ」ですから削ります。但し「評価式」は消しません。評価式も「何かを実行する」からです。

◎1
void disp_sqrt(double n)
{
if(n<=0.0) goto end_func;
printf("%f の平方根=%f\n",n,sqrt(n));
end_func:
}

◎2
void disp_sqrt(double n)
{
n<=0.0;
}

◎3
void disp_sqrt(double n)
{
if(n<=0.0) goto end_func;
printf("%f の平方根=%f\n",n,sqrt(n));
end_func:
}

代入もしない、i++などのインクリメントもしない式は、副作用が無いので、削っても構いません。削ります。

◎1
void disp_sqrt(double n)
{
if(n<=0.0) goto end_func;
printf("%f の平方根=%f\n",n,sqrt(n));
end_func:
}

◎2
void disp_sqrt(double n)
{
}

◎3
void disp_sqrt(double n)
{
if(n<=0.0) goto end_func;
printf("%f の平方根=%f\n",n,sqrt(n));
end_func:
}

結果、◎1と◎3は、同じプログラムになりました。当然、同じ動作をします。◎2は、関数の中身が何も無くなりました。

これで、◎1と◎3が同じ動きをして、◎2が何も表示しないのが、良く判ると思います。

以下、蛇足。

goto文は「上記のような、プログラムの流れを説明する時だけ」にしか使いません。

C言語では、構造化プログラムのルールを覆すことになるので、goto文を使用してはいけません。
    • good
    • 0
この回答へのお礼

ご丁寧に返答ありがとうございます。

>if(n<=0.0) return;」だけがif文です。printf関数の部分は、if文と
>は無関係です。

以上の記述納得できました。
---------------------------------------------
if(n<=0.0){
  return;
}
  printf("%f の平方根=%f\n",n,sqrt(n));
--------------------------------------------
--------------------------------------------
if(n<=0.0){
  return;
}
else{
  printf("%f の平方根=%f\n",n,sqrt(n));
}
--------------------------------------------

{ }を付けると以上のような記述になると納得できました。
記述にあったパターン覚えておきます。

「else {;}」の「{;}」の記述は省略して書いているという事なのですかね?ここが、初めて見たのでよくわかりませんでした。

お礼日時:2009/02/12 21:40

{}がないとif文に対してreturn;だけが1文となり{}があると{}内に何行


あろうが{}自体が1文になり、if文の条件が満たされないとその次の1文
は無視されるということですね。
制御文はその次の「1文」しか制御しないという理屈は正しいという解釈に
なりますね。
    • good
    • 0
この回答へのお礼

ご返答ありがとうございました。
確かに{ }が無いという事は、return文までしか制御されないという事ですよね。それに気付けてよかったです。ありがとうございました。

お礼日時:2009/02/12 22:29

>上の記述の場合、「if(n<=0.0)」という条件のくくりしかないため、


>それ以外の条件の場合が呼び出されても、何も起きなかったといった
>感じですかね?

そのとおりです。
それはさておき、今回やりたいことを最も端的に表現するならば、

if (n > 0.0) {
printf("%f の平方根=%f\n",n,sqrt(n));
}

こうだと思います。n >= 0.0 にする方がいいのではないかと
いう話もありますが…。
    • good
    • 0
この回答へのお礼

>if (n > 0.0) {
>printf("%f の平方根=%f\n",n,sqrt(n));
>}

確かに以上の記述で正常に動きますし、その方が良いと思いました。
参考書で、今勉強している分野が、関数によるreturn文を使う事に関してでしたので、無理やり
if(n<=0.0) return;
printf("%f の平方根=%f\n",n,sqrt(n));
としてreturn文を使っているのかなと思いました。

お礼日時:2009/02/12 22:27

>自分はif文が文が1つでもカッコ{ }を付けたい考えなので、


そういったコーディングスタイルなら、
void disp_sqrt(double n)
{
if(n<=0.0){
return;
}
printf("%f の平方根=%f\n",n,sqrt(n));
}
こうなりますね。
    • good
    • 0
この回答へのお礼

ご返答ありがとうございます。

-------------------------------------------
void disp_sqrt(double n)
{
if(n<=0.0){
  return;
}
else{
  printf("%f の平方根=%f\n",n,sqrt(n));
}
}
--------------------------------------------
以上のような記述でも良いですが、if文の中にある文はreturn文だけですので、
---------------------------------------
void disp_sqrt(double n)
{
if(n<=0.0){
return;
}
printf("%f の平方根=%f\n",n,sqrt(n));
}
--------------------------------------
以上の記述でもいいんですよね。ありがとうございました。

お礼日時:2009/02/12 21:47

◎1


>if(n<=0.0) return;    // nが0.0以下ならば何もせずに終わる
>printf("%f の平方根=%f\n",n,sqrt(n));   // そうでなければ平方根を出力する

これは、
if(n<=0.0) {
return;
}
else {
printf("%f の平方根=%f\n",n,sqrt(n));
}
と同じです。

◎2
>if(n<=0.0){     // nが0.0以下ならば
>return;       // 戻る
>printf("%f の平方根=%f\n",n,sqrt(n));  // この文は実行されない
>}

nが0.0を超えたときの処理が書いてありません。
    • good
    • 0
この回答へのお礼

ご返答ありがとうございます。

if(n<=0.0){
return;
printf("%f の平方根=%f\n",n,sqrt(n));
}

上の記述の場合、「if(n<=0.0)」という条件のくくりしかないため、それ以外の条件の場合が呼び出されても、何も起きなかったといった感じですかね?

お礼日時:2009/02/12 07:15

{と}の範囲を実行範囲といます。


◎1は、関数 disp_sqrt(double n)自身の実行範囲({})だけで
if文の実行範囲({})はありません。よって、
if文の条件を満たす場合は何も処理せす関数を抜ける。
if文の条件を満たさない場合にprintf文が処理されます。
◎2は、関数 disp_sqrt(double n)自身の実行範囲({})と
if文の条件を満たす場合の実行範囲({})があります。よって、
同一実行範囲にある return文とprintf文は
先にreturn文が処理されるので、printf文が処理実行されること
は絶対無いのです。
    • good
    • 0
この回答へのお礼

ご返答ありがとうございます。
◎1で見ると、「if(n<=0.0) return;」で0以下なら処理を終了で呼び出し側に戻る。
0以上なら「printf("%f の平方根=%f\n",n,sqrt(n));」を実行。
といった感じですかね?

0以下ならなら呼び出し側に戻る。という事は、return文とprintf文の2つの文があると別にとらえなくてもいいという考えでいいのですかね?

お礼日時:2009/02/12 07:08

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!

このQ&Aを見た人はこんなQ&Aも見ています


おすすめ情報