【C言語】
if(a||b&&c)が【&&】で改行してたら怪しい

if(a||b&&c)に括弧を付けて

warning: suggest parentheses around ‘&&’ within ‘||’

警告:A||B&&C
  A||(B&&C) と同じで
  (A||B)&&C と違う
[-Wparentheses]


■改行位置から推測される結合

int f1(int a,int b,int c)
{
    if(a &&      //ダメ:改行位置から推測される結合が違う
       b || c) {
        return  1;
    }
    return  0;
}

(a&&b||c)は
((a&&b)||c) と同じで
(a&&(b||c))と違います。

この関数では改行位置から推測される
プログラマが期待する結合力と
コンパイラが解釈する結合力が違います。
改行の位置で結合力は変わりません。
丸カッコ()を使って結合力を明確にしましょう。


■プログラマが期待する結合

int f2(int a,int b,int c)
{
    if( a &&
       (b || c)   ) {//プログラマが期待する結合???
        return  1;
    }
    return  0;
}

改行位置から推測すると
プログラマは(b||c)で結合する事を期待しているようです。


■コンパイラが解釈する結合

int f3(int a,int  b,int c)
{
    if((a && b) || //コンパイラはこう解釈する
        c) {
        return  1;
    }
    return  0;
}

コンパイラの解釈はプログラマの期待と違い(a&&b)で結合します。


■閏年の判定式と括弧の位置

閏年の判定式のカッコはどちらに付けるのが適切でしょうか?

#include    <stdio.h>
#include    <stdlib.h>
#include    <time.h>
#include    <limits.h>
//参考:プログラミング言語C
#define isleap1(Y)  (Y % 4 == 0 &&  Y % 100 != 0 || Y % 400 == 0)
//参考:linux-time.h __isleap
#define isleap2(Y)  (Y % 4 == 0 && (Y % 100 != 0 || Y % 400 == 0))
//参考:gcc -Wall の警告指示
#define isleap3(Y) ((Y % 4 == 0 &&  Y % 100 != 0)|| Y % 400 == 0)
int main(void)
{
    for(int y = SHRT_MIN ; y < SHRT_MAX ;y++){
        if(isleap1(y) != isleap2(y)) {
            printf("1!=2 = %d\n",y);
            abort() ;
        }
        if(isleap2(y) != isleap3(y)) {
            printf("1!=3 = %d\n",y);
            abort() ;
        }
        if(__isleap(y) != isleap1(y)){
            printf("head !=1 = %d\n",y);
            abort() ;
        }
    }
}

ちょっと意外ですがどちらでも可。
どちらにカッコを付けても同じ結果になります。

しかし次に示すようにいつでも同じ結果になるわけではないのでカッコつけましょう。


■A||(B&&C)と(A||B)&&Cが違う例

#include <stdio.h>
#include <stdbool.h>
void    diff(bool x,bool y,bool z){
    bool    e1 = (x||y)&&z  ;
    bool    e2 =  x||(y&&z) ;
    if(e1 != e2){  
        printf("違う\n");
        printf("(%d||%d)&&%d = %d\n",x,y,z,e1);
        printf(" %d||(%d&&%d)= %d\n",x,y,z,e2);
    }
}
int main(void){
    for(int x = 0;x <= 1;x++){
        for(int y = 0;y <= 1;y++){
            for(int z = 0;z <= 1;z++){
                diff(x,y,z);        
            }
        }
    }
}

参考:

(year) %4 == 0 && ((year) %100 != 0 || (year) %400 == 0)

https://linuxjm.osdn.jp/html/LDP_man-pages/man3/dysize.3.html

EXP02-C. 論理 AND 演算子および論理 OR 演算子のショートサーキット動作について注意する

https://www.jpcert.or.jp/sc-rules/c-exp02-c.html