【C言語】
うるう年判定式を1行で書く

うるう年判定式

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

警告:X&&Y||Zは(X&&Y)||Zと丸括弧を付けて
[-Wparentheses]


■閏年判定関数

int isleap1(int year)   {
    if (year % 4 == 0) {
        if (year % 100 == 0) {
            if (year % 400 == 0) {
                return 1;//"閏年です";
            } else {
                return 0;//"閏年ではありません";
            }
        } else {
            return 1;//"閏年です";
        }
    } else {
        return 0;//"閏年ではありません";
    }
}

記述量が多いので非推奨


■早期リターン版


int isleap2(int year)   {
    if (year % 4) 
        return 0;   //"閏年ではありません";
    if (year % 100) 
        return 1;   //"閏年です";
    if (year % 400) 
        return 0;   //"閏年ではありません";
    return 1;   //"閏年です";
}

早期リターンで分かりやすいかというと微妙


■1行論理式版


int isleap3(int year){
    if((year%4==0 && year%100!=0) || year%400==0){
        return 1;   //"閏年です";
    }else{
        return 0;   //"閏年ではありません";
    }
}

論理式を一行にまとめてちょっとすっきり


■マクロ版


#define isleap4(Y) (\
    ((Y)%4==0 && (Y)%100!=0) || (Y)%400==0\
)

丸括弧がうるさいのであまり推奨しない


■関数版(丸括弧無し版)


static inline bool isleap5(int 西暦) 
{
    return  西暦%4==0 && 西暦%100!=0 || 西暦%400==0;  
}

コンパイル時警告有りで非推奨

このスタイルは
聖典プログラミング言語Cでは
leap = year%4==0 && year%100!=0 || year%400==0
と紹介されています。

でも
gccやclangに丸括弧を付けてと警告されます。

ちょっと簡略化けして
leap = (x&&Y)||Z; 

leap = X&&(Y||Z);
は、
どちらが正しいでしょうか?

この閏年判定の論理式の場合
どちらも正しいとWikipedia
説明されています。

丸括弧の位置で動作が変わる論理式もあるので
丸括弧を付けるようにしましょう。


■丸括弧有り版:X&&(Y||Z)

static inline bool isleap6(int 西暦) 
{
    return  西暦%4==0 && (西暦%100!=0 || 西暦%400==0);  
}

■丸括弧有り版:(X&&Y)||Z

static inline bool isleap7(int 西暦) 
{
    return  (西暦%4==0 && 西暦%100!=0) || 西暦%400==0;  
}

■まとめ(コンパイル可能)

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

//閏年判定関数(記述量が多いので非推奨)
int isleap1(int year)   {
    if (year % 4 == 0) {
        if (year % 100 == 0) {
            if (year % 400 == 0) {
                return 1;//"閏年です";
            } else {
                return 0;//"閏年ではありません";
            }
        } else {
            return 1;//"閏年です";
        }
    } else {
        return 0;//"閏年ではありません";
    }
}
//閏年判定関数(早期リターン版)
int isleap2(int year)   {
    if (year % 4) 
        return 0;   //"閏年ではありません";
    if (year % 100) 
        return 1;   //"閏年です";
    if (year % 400) 
        return 0;   //"閏年ではありません";
    return 1;   //"閏年です";
}
//閏年判定関数(1論理式版)
int isleap3(int year){
    if((year%4==0 && year%100!=0) || year%400==0){
        return 1;   //"閏年です";
    }else{
        return 0;   //"閏年ではありません";
    }
}
//閏年判定マクロ(丸括弧がうるさいので非推奨) 
#define isleap4(Y) (\
    ((Y)%4==0 && (Y)%100!=0) || (Y)%400==0\
)
//閏年判定関数(コンパイラの警告有りで非推奨) 
static inline bool isleap5(int 西暦) 
{
    return  西暦%4==0 && 西暦%100!=0 || 西暦%400==0;  
}
//閏年判定関数(X&&(Y||Z)版)
static inline bool isleap6(int 西暦) 
{
    return  西暦%4==0 && (西暦%100!=0 || 西暦%400==0);  
}
//閏年判定関数(推奨 (X&&Y)||Z   版)
static inline bool isleap7(int 西暦) 
{
    return  (西暦%4==0 && 西暦%100!=0) || 西暦%400==0;  
}

int main(void){
    for(int 西暦 = 1582 ; 西暦 <= 2038 ;西暦++){
        char    s[8][256];
        
        //基本的な書き方
        if(isleap1(西暦)){
            sprintf(s[1],"%d年は閏年です",西暦);
        } else {
            sprintf(s[1],"%d年は閏年ではありません",西暦);
        }
        
        //途中経過
        sprintf(s[2],"%d年は%s",西暦,isleap2(西暦)?"閏年です":"閏年ではありません");
        sprintf(s[3],"%d年は%s",西暦,isleap3(西暦)?"閏年です":"閏年ではありません");
        sprintf(s[4],"%d年は%s",西暦,isleap4(西暦)?"閏年です":"閏年ではありません");
        sprintf(s[5],"%d年は%s",西暦,isleap5(西暦)?"閏年です":"閏年ではありません");
        sprintf(s[6],"%d年は%s",西暦,isleap6(西暦)?"閏年です":"閏年ではありません");
        
        //推奨する書き方
        sprintf(s[7],"%d年は%s",
            西暦,
            isleap7(西暦)?
                "閏年です"
                :
                "閏年ではありません"
        );
        //閏年判定結果の表示  
        puts(s[7]);
        
        //検算:s[1]~s[7]まで全部同じになるはず
        if(strcmp(s[1],s[2]) != 0)  assert(0);
        if(strcmp(s[1],s[3]) != 0)  assert(0);
        if(strcmp(s[1],s[4]) != 0)  assert(0);
        if(strcmp(s[1],s[5]) != 0)  assert(0);
        if(strcmp(s[1],s[6]) != 0)  assert(0);
        if(strcmp(s[1],s[7]) != 0)  assert(0);
    }
}

参考:

C-FAQ 20.32

isleap