プロが教えるわが家の防犯対策術!

○if文の判定条件への理解が弱いため、下記プログラムを作成し、
疑問があったため、質問をしたいと思っております。

#include<stdio.h>
#include<string.h>

int main()
{
char moji[]= "A";
/***********************************************************/
if( 0 != strcmp( moji , "A" ) || strcmp( moji , "B" ) )
^^(1)
/***********************************************************/
{
printf( "文字不一致" );
}
else
{
printf( "文字一致" );
}

return (0);
}

○質問
「/*/」で囲ったif文の判定箇所になりますが、
この判定条件は"配列mojiに格納されている値が"A",または"B"以外の"
ときに、画面上に「文字不一致」を出力します。そうでなければ
「文字一致」を出力します。

今回、配列mojiには"A"が格納されており、「文字一致」が画面に
出力されるはずですが、「文字不一致」が出力されてしまいます。

判定条件が誤っていると思い、色々と試したところ、(1)の箇所を
「&&」にしたときに、「文字不一致」が出力されます。

「||」(論理和)と「&&」(論理積)が理解できていないと思われ、
この部分を交えて、何故「&&」にしないと「画面不一致」と出力
されないのかを、ご教授の程お願い致します。

A 回答 (7件)

No.3 です。



---------------------------
(1)は配列mojiにAが格納されていない場合
(2)は配列mojiにBが格納されていない場合

《 略 》

(3)では(1)と(2)で出力された値の論理積(偽と真の論理積)を取り、
偽となるので、else側に移り、「文字一致」が出力される。
---------------------------
この理解で、考え方としては合っています。
普段はこれでOKです。

ただ、C言語ではもうひとつ別の問題があります。
No.4 の方の回答でも触れられていますが。

もともと、&& や || の性質として、
&& が、「真」になるのは、両方が真の時だけ、つまり、左側が「偽」なら、右側が何であっても、「偽」
|| が、「偽」になるのは、両方が偽の時だけ、つまり、左側が「真」なら、右側が何であっても、「真」
という事があります。

C言語ではこれを積極的に取り入れており、
1) && は、左側が「偽」であれば、右側は調べなくても全体として「偽」になるに決まっている。したがって、右側は調べない。
2) || は、左側が「真」であれば、右側は調べなくても全体として「真」になるに決まっている。したがって、右側は調べない。

という動きをします。
ですから、このケースでは、(1)の部分が「偽」となった時点で、全体が偽になるのが明らかです。ですから、(2)の部分は、調べません。

よって、C言語の動きに沿っていえば、
(1)の部分が「偽」となる。だから、((2)を調べるまでもなく)(3)全体として、「偽」となり、else 部分が実行される
という流れになります。

もっとも、これは、結果としては真偽表と矛盾はしないので、補足にお書きになったような理解でも普通はかまいません。
ただし、&& や || 以降の部分を調べることが、他の影響を与える時(例えば、strcmp() の中に(たとえ話です)「一致した」とかいう出力を行う部分があったら、この出力が1回しかでなかったり(&& の前で「偽」が決定した時)2回出たり(&& の前が「真」だったので、後ろも調べた時)というように、この影響を考慮しなければならない場合もあります。
    • good
    • 1
この回答へのお礼

このコメントで理解することができました。
確認までしていただき、ありがとうございました^^
また、ご教授お願い致します。

お礼日時:2007/04/15 17:13

まず実行結果ですが、


上記プログラムの実行結果は「文字不一致」が表示されるのですよね?
ページ上うまく出ていないので良くわかりませんが、
論理和の代りに論理積をを指定すると「文字一致」が表示される
ということかと思います。

解説します。
if( 0 != strcmp( moji , "A" ) || strcmp( moji , "B" ) )
これは正確に書けば↓こういう式ですよね。
if (0 != strcmp(moji,"A") || 0 != strcmp(moji,"B"))

strcmpをさらに抽象的に書いてみますと
if (moji != "A" || moji != "B")
となりますね。

C/C++は式の評価が左から行われます。
また、論理和(||)演算子では、
演算子自体の優先度が低いので 左側の式が先に評価されます。
左側と右側に分けて考えて見ましょう。
左側の moji != "A" ですが、mojiは"A"であるので評価は偽です。
論理和(||)演算子は左側式の結果が偽なら右辺の評価を行います。
次に評価されるのは moji != "B" ですが、
mojiは"A"なので "A" != "B" となり評価結果は真となりますよね。
つまり、
if (moji != "A" || moji != "B")
     ↓
  if (偽 || 真)
ですから、論理和(||)演算子の結果が真となるので
「文字不一致」が表示されます。
日本語にすれば「mojiが"A"でなく、または、"B" でない」です。
"B"でないことは確かなので、このプログラム通りの動作をしていると言えます。

代りに論理条件をAND(論理積)とした場合、評価結果は
if (moji != "A" && moji != "B")
     ↓
  if (偽 && 真)
となります。論理和と違い、論理積演算子は、演算子の両側が真でなければ論理積演算子の結果が真にならないので、
「文字一致」のほうが表示されます。
if (moji != "A" && moji != "B")
を日本語にすれば「mojiが"A"でもなくかつBでもない」、
「かつ」が論理積に相当しますね。

論理和(||)演算子の動作は、コンパイラ内部の動作からみると、
論理和演算子の左側を評価して、真であれば、右側の評価はせずに
結果として真を返します。
左側の結果が偽のときにのみ右側の式も評価して、その結果を
論理和演算子の結果とします。
逆に、論理積(&&)演算子は、
論理積演算子の左側を評価して、偽であれば、右側の評価はせずに、
論理積演算子の結果として返します。

懸案の評価式ですが "A" でも "B" でもないときに「文字不一致」と出したいのであれば、
if( 0 != strcmp( moji , "A" ) && strcmp( moji , "B" ) )
とするのが妥当ではないでしょうか。

補足ですが、strcmp関数について。
一致するときは0を返します。
↓こういう感じで覚えておくといいと思います。
strcmp(a,b) != 0 → a != b
strcmp(a,b) == 0 → a == b
strcmp(a,b) > 0 → a > b
strcmp(a,b) < 0 → a < b

お役にたちましたでしょうか。
    • good
    • 1

★回答者 No.4 です。

最初にお詫びします。→回答を一部、間違いました。
・下のが修正した正しい内容です。
 間違い⇒『moji が A なら真』
 正しい⇒『moji が A 以外なら真』
 
 if ( (moji が A 以外なら真) || (moji が B 以外なら真) ){
  (1)
 }
 else{
  (2)
 }
 となります。

★本題、『strcmp』関数について解説します。
 この関数は2つの文字列の内容を比較します。そして、次のような値を返します。
 引数が strcmp( 文字列A, 文字列B ); の場合、
 (1)文字列Aは文字列Bより小さい⇒0以下(文字列A<文字列B)
 (2)文字列Aは文字列Bと全く同じ⇒0  (文字列A=文字列B)
 (3)文字列Aは文字列Bより大きい⇒0以上(文字列A>文字列B)
 という値を返します。3つの状態 0以下、0、0以上です。
・それで文字列の一致する戻り値は 0 の時です。
 このことから次のようになります。
 『strcmp(moji,"A")』⇒この式が『真』になるのは、moji が A 以外の場合(つまり不一致)
 『!strcmp(moji,"A")』⇒この式が『真』になるのは、moji が A の場合(つまり一致)
 となります。また、
 『strcmp(moji,"A") != 0』⇒『strcmp(moji,"A")』と同じ意味(不一致)
 『strcmp(moji,"A") == 0』⇒『!strcmp(moji,"A")』と同じ意味(一致)
 としても同じです。
・上記のを踏まえると KGM さんの質問にある
 『if ( (0 != strcmp(moji,"A")) || strcmp(moji,"B") )』の意味は
 『if ( (moji が A 以外のとき真) || (moji が B 以外のとき真) )』と解釈できて
 moji に A がセットされている状態では、常に if 直下のブロックである『文字不一致』が
 表示されたのです。

ロジック:
if ( (moji が A 以外のとき真) || (moji が B 以外のとき真) ){
 『文字不一致』を表示
}
else{
 『文字一致』を表示
}

組み合わせ:論理和タイプ
moji が A のとき⇒if ( 偽 || 真 )ですが、論理和であるため if 内は『真』となり『文字不一致』を表示
moji が B のとき⇒if ( 真 || 偽 )ですが、論理和であるため if 内は『真』となり『文字不一致』を表示
moji が C のとき⇒if ( 真 || 真 )ですが、論理和であるため if 内は『真』となり『文字不一致』を表示
  :
moji が Z のとき⇒if ( 真 || 真 )ですが、論理和であるため if 内は『真』となり『文字不一致』を表示

組み合わせ:論理積タイプ
moji が A のとき⇒if ( 偽 && 真 )ですが、論理積であるため if 内は『偽』となり『文字一致』を表示
moji が B のとき⇒if ( 真 && 偽 )ですが、論理積であるため if 内は『偽』となり『文字一致』を表示
moji が C のとき⇒if ( 真 && 真 )ですが、論理積であるため if 内は『真』となり『文字不一致』を表示
  :
moji が Z のとき⇒if ( 真 && 真 )ですが、論理積であるため if 内は『真』となり『文字不一致』を表示

最後に:
・何かコメント下さいな。1行でもいいです。寂しいです。
 分からなかったらば、また別の回答を出します。
・以上。
    • good
    • 0
この回答へのお礼

返信が遅れてしまい、申し訳ございません。
基本情報技術者試験と業務のため、レスをかくことができませんでした。

内容はほとんど理解することができました。
この式自体があやふや過ぎたとこもあり、自分自身が混乱して
しまったものだと思ってしまいます。混乱するとダメですね^^

また、わからないことがありましたら、ご教授お願い致します。

お礼日時:2007/04/15 17:12

★何故なのかを解説します。


・KGM さんの if-else を分かりやすくすると次のようになります。
 if ( (moji が A なら真) || (moji が B 以外なら真) ){
  (1)
 }
 else{
  (2)
 }
 このプログラムでは、
 moji が A のとき⇒(1)を処理(文字不一致)
 moji が B のとき⇒(2)を処理(文字一致)
 moji が C のとき⇒(1)を処理(文字不一致)
     :
 moji が Z のとき⇒(1)を処理(文字不一致)
 という処理が行われます。
・このことから moji が A のときは『文字不一致』が表示されて正しいのです。
 次に『||』を『&&』に変更すると、
 if ( (moji が A なら真) && (moji が B 以外なら真) ){
  (1)
 }
 else{
  (2)
 }
 このプログラムでは、
 moji が A のとき⇒(1)を処理(文字不一致)
 moji が B のとき⇒(2)を処理(文字一致)
 moji が C のとき⇒(2)を処理(文字一致)
     :
 moji が Z のとき⇒(2)を処理(文字一致)
 という処理が行われます。
・このことから moji が A のときだけ『文字不一致』が表示されです。
・つまり、プログラムの考えそのものが矛盾したおかしな記述なのです。
 上記を参考にしてプログラムし直して下さい。

その他:
・論理和『||』と論理積『&&』の含まれた式は、最初に真または、偽となったらば、
 その次の式は評価しないで if ブロック、else ブロックに制御が移ります。
 このことから、moji に A がセットされていると常に『真』の状態になるため
 if ブロックの(1)である『文字不一致』が表示されてしまったのです。
・また、『&&』に変更した後は、moji が A 以外では常に『偽』となるためすべて
 else ブロックの(2)である『文字一致』が表示されていますのです。
・C/C++言語では、
 論理和(||)のとき、最初に『真』になった式があれば、直ぐに if ブロックに制御を
 論理積(&&)のとき、最初に『偽』になった式があれば、直ぐに else ブロックに制御を
 移動させてしまいます。この動作に注意!
・以上です。→上記の動作を踏まえてプログラミングし直して下さい。
    • good
    • 0

おそらく正解は、|| (または)を使うのであれば、



if ((strcmp( moji , "A" ) == 0) || (strcmp( moji , "B" ) == 0))
printf( "文字一致" );
else
printf( "文字不一致" );

です。

もとの形に近づけようとすれば、
if ((strcmp( moji , "A" ) != 0) && (strcmp( moji , "B" ) != 0))
printf( "文字不一致" );
else
printf( "文字一致" );
です。

さて、順次考えてみましょう。

まず、
> 配列mojiに格納されている値が"A",または"B"以外の"ときに、画面上に「文字不一致」を出力します

これはどういう意味でしょうか?
別に揚げ足取りではなくて、

a)(moji の値が "A" である)または、(moji の値が "B" 以外) の時に、「文字不一致」
b)moji の値が ("A" または "B") 以外の時に、「文字不一致」

どちらの意味にも取れます。
ここでは、「

> 配列mojiには"A"が格納されており、「文字一致」が画面に出力されるはずですが

とのことから、b)の意味だとします。

この場合、C言語のレベルでは、

b)( (moji の値が "A") または (moji の値が "B")) 以外の時に、「文字不一致」
となります。
つまり、strcmp() の結果を、毎回比較する必要があるということです。

これをこのまま書くと、

if (! ((strcmp(moji, "A") == 0) || (strcmp(moji, "B") == 0) ) printf("文字不一致");
となります。

~ 以外の時 というのを、そのまま表現するために、最初に ! (否定演算子)がついています。
この ! を取り除くには、

1)もともと条件が反対なのだから処理を入れ替える

if ((strcmp(moji, "A") == 0) || (strcmp(moji, "B") == 0) printf("文字一致");

2)論理式を反転させて

if ((strcmp(moji, "A") != 0) && (strcmp(moji, "B") != 0) printf("文字不一致");

のいずれかに変形できます。

== と != , || と &&, 文字一致 と 文字一致 の関係に注意してください。

もともと、

moji が ("A" または "B" )でないとき
というのは、つまり、"A" でも "B" でもないときですから、
moji が "A" でない、かつ "B" でない
となります。


最後に、もともとの式が、エラーにもならずとにかく動いてしまうのは、

if( 0 != strcmp( moji , "A" ) || strcmp( moji , "B" ) )

が、
(0 != strcmp( moji , "A" )) または strcmp( moji , "B" )
という意味だからです。
今回の場合、
(0 != strcmp( moji , "A" )) の部分は、moji が "A" なので、意味としては、
「偽」になります。
ところが、後半の strcmp( moji , "B" ) は、strcmp() の定義から、0 でない
結果となり、こちらは、if() の判定では「真」となります。

ですから、偽 || 真 で、if() 全体の結果は、「真」となり、if() の直後にある、「文字不一致」が表示されます。
一方で、|| を && に置き換えると、偽 && 真 で、if() 全体の結果は、「偽」となり、else の次にある、「文字一致」

※ここで、「偽」とか「真」とか表現しているのは、C言語の意味では厳密には間違っていますが。

この回答への補足

下の箇所の説明のお陰で理解できたと思います。

ここで、確認したいことがあるのですが、

if( ( 0 != strcmp( moji , "A" )) || ( 0 != strcmp( moji , "B" )) )
^^^^^^^^^^^^^^^^^^^^^^^^^^(1) ^^^^^^^^^^^^^^^^^^^^^^^^^^^(2)

このようにした場合、

(1)はmojiにAが設定されているので、0以外の値が出力されます。
#この箇所だけで判定すると文字不一致が出力されます。

(2)はmojiにAが出力されていますので





if ((strcmp( moji , "A" ) != 0) && (strcmp( moji , "B" ) != 0))
^^^^^^^^^^^^^^^^^^^^^^^^^^(1) ^^^^^^^^^^^^^^^^^^^^^^^^^(2)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^(3)
{
printf( "文字不一致" );
}
else
{
printf( "文字一致" );
}


この部分を確認させて下さい。


(1)は配列mojiにAが格納されていない場合
(2)は配列mojiにBが格納されていない場合

を表します。
プログラム上では、(1)はmojiにAが格納されているので一致し、出力される値は0となり、「 != 0」とは
一致しないので、結果は「偽」となる。

(2)はmojiにAが格納され、Bとマッチしていないので一致せず、出力される値は0以外(1)となり、「 != 0」と
一致するので、結果は「真」となる。

(3)では(1)と(2)で出力された値の論理積(偽と真の論理積)を取り、
偽となるので、else側に移り、「文字一致」が出力される。

という認識であっているでしょうか。
理論は理解できているようなのですが、いまいちしっくりこない状態です。

補足日時:2007/04/09 14:56
    • good
    • 0

プログラムと質問文が一致しないないようです。


strcmpは一致したときに0を戻し、不一致のときは0以外を戻しますので、
「値が"A",または"B"以外」という条件ならば、
if( 0 == strcmp( moji , "A" ) || 0 != strcmp( moji , "B" ) )
となります。
プログラムのほうが正しいのであれば、
「値が"A"以外、または???」
となります。
また"||"を"&&"に変えた場合、
「値が"A"以外、かつ???」
となります。
もう一度内容を整理されたほうがよろしいかと思います。
    • good
    • 0
この回答へのお礼

整理した結果、内容を理解することができました。
お手数おかけしました。

お礼日時:2007/04/15 17:16

>判定条件は"配列mojiに格納されている値が"A",または"B"以外の"


ときに、画面上に「文字不一致」を出力
日本語の文章ですと「または」とあるので、「||」(論理和)を用いてしまったようですが、
プログラムとして解釈する場合には、
配列mojiに格納されている値が"A"以外かつ"B"以外
と記述しなくてはなりません。
ということで、「&&」(論理積)を用いるのが正しいのではないでしょうか。
Cに詳しくはないので参考までに。
    • good
    • 0
この回答へのお礼

自分が意図しているようにならないと、大混乱してしまいました。
ご迷惑おかけしました。

お礼日時:2007/04/15 17:15

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