【C言語】
西暦から干支を求める方法
-2025年➡巳(み)年に変換-

■この記事の概要

この記事では、C言語のポインタの便利さについて解説し、strcpy関数と比較しています。ポインタを使った直接的なアドレス操作の効率性を示し、無駄なコピー操作を回避する手法を紹介。また、十二支(干支)をテーマにしたサンプルコードを使い、ポインタや文字列処理を実践的に学べる例も掲載されています。C言語の基礎知識を応用し、効率的なプログラミング方法が理解できる内容です。


■strcpyを使って西暦から干支を求める

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

void 西暦干支変換(int 西暦,char *干支){
    switch ( 西暦%12 ) {
    case  0: strcpy(干支,"申(さる)");     break;
    case  1: strcpy(干支,"酉(とり)");     break;
    case  2: strcpy(干支,"戌(いぬ)");     break;
    case  3: strcpy(干支,"亥(い)");       break;
    case  4: strcpy(干支,"子(ね)");       break;
    case  5: strcpy(干支,"丑(うし)");     break;
    case  6: strcpy(干支,"寅(とら)");     break;
    case  7: strcpy(干支,"卯(う)");       break;
    case  8: strcpy(干支,"辰(たつ)");     break;
    case  9: strcpy(干支,"巳(み)");       break;
    case 10: strcpy(干支,"午(うま)");     break;
    case 11: strcpy(干支,"未(ひつじ)");   break;
    default: assert(!"ここには来ない");     break;
    }
}
int main(void){
    const int 今年=2025;
	for(int 西暦 = 今年;西暦<=今年+12;西暦++){
        char    干支[256];
        西暦干支変換(西暦,干支);
        printf("%4d年の干支は%s\n",西暦,干支);
    }
}

意図した動きをしますが
人間が記述する量が多く、
コンピュータのための機械語も効率が悪いので処理速度が遅いです。

strcpyで文字列を出力配列に
わざわざコピーしていますが無駄です


■文字列のアドレスを直接使って西暦から干支を求める

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

char *西暦干支変換(int 西暦){
    switch ( 西暦%12 ) {
    case  0: return "申(さる)";
    case  1: return "酉(とり)";
    case  2: return "戌(いぬ)";
    case  3: return "亥(い)";
    case  4: return "子(ね)";
    case  5: return "丑(うし";
    case  6: return "寅(とら)";
    case  7: return "卯(う)";
    case  8: return "辰(たつ)";
    case  9: return "巳(み)";
    case 10: return "午(うま)";
    case 11: return "未(ひつじ)";
    default: assert(!"ここには来ない");
    }
}
int main(void){
    const int 今年=2025;
	for(int 西暦 = 今年;西暦<=今年+12;西暦++){
        char    *干支 = 西暦干支変換(西暦);
        printf("%4d年の干支は%s\n",西暦,干支);
    }
}

strcpyで文字列を全部コピーしないで
文字列のアドレスだけを返却しているので
strcpy版より効率が良いです。


ポインタポインタを使って西暦から干支を求める

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

void 西暦干支変換(int 西暦,char **干支){
    switch ( 西暦%12 ) {
    case  0: *干支 = "申(さる)";  break;
    case  1: *干支 = "酉(とり)";  break;
    case  2: *干支 = "戌(いぬ)";  break;
    case  3: *干支 = "亥(い)";    break;
    case  4: *干支 = "子(ね)";    break;
    case  5: *干支 = "丑(うし";    break;
    case  6: *干支 = "寅(とら)";  break;
    case  7: *干支 = "卯(う)";    break;
    case  8: *干支 = "辰(たつ)";  break;
    case  9: *干支 = "巳(み)";    break;
    case 10: *干支 = "午(うま)";  break;
    case 11: *干支 = "未(ひつじ)";break;
    default: assert(!"ここには来ない");
    }
}
int main(void){
    const int 今年=2025;
	for(int 西暦 = 今年;西暦<=今年+12;西暦++){
        char    *干支 = NULL ; 
        西暦干支変換(西暦,&干支);
        printf("%4d年の干支は%s\n",西暦,干支);
    }
}

●ポインタポインタ**を使った例です。
少々難解です。

■配列を使って西暦から干支を求める

#include <stdio.h>
#include <string.h>
#include <assert.h>
char *西暦干支変換(int 西暦){
    static char *干支[] ={
        [0]="申(さる)"    ,   
        [1]="酉(とり)"    ,   
        [2]="戌(いぬ)"    ,   
        [3]="亥(い)"      ,     
        [4]="子(ね)"      , 
        [5]="丑(うし)"    , 
        [6]="寅(とら)"    ,
        [7]="卯(う)"      ,         
        [8]="辰(たつ)"    , 
        [9]="巳(み)"      ,   
        [10]="午(うま)"   ,  
        [11]="未(ひつじ)" 
    };
    return      干支[西暦%12] ;
}
int main(void){
    const int 今年=2025;
	for(int 西暦 = 今年;西暦<=今年+12;西暦++){
        char    *干支 = 西暦干支変換(西暦);
        printf("%4d年の干支は%s\n",西暦,干支);
    }
}

●switch文が不要になりました。
このコードは記述量も少なく、
処理速度もおそらく最速です。


■なぜstrcpyよりポインタが早いのか?

寿限無落語の文字列を関数引数で受け渡す歳に
strcpy版とpointer版を比較して、
ポインタの便利さを学習しましょう。

●strcpy版は遅いです

#include <stdio.h>
#include <string.h>
//長い寿限無落語
static char input[] = 
    "Jugemu Jugemu," 
    "Gokō no Surikire," 
    "Kaijarisuigyo no Suigyōmatsu," 
    "Ungyōmatsu," 
    "Fūraimatsu," 
    "Kuunerutokoro ni Sumutokoro," 
    "Yaburakōji no Burakōji," 
    "Paipo Paipo," 
    "Paipo no Shūringan," 
    "Shūringan no Gūrindai," 
    "Gūrindai no Ponpokopī no Ponpokona no Chōkyūmei,"
    "Chōkyūmei no Chōsuke";
void use_strcpy(char *output)
{
    puts("■例えば:長い寿限無落語のCDの内容をコピーするのがstrcpy板");
    printf("%sは%zu byte の転送命令が生成されます\n",__func__,sizeof(input));
    strcpy(output,input);
}
int main(void){
    char    buf[1024] ={0};
    use_strcpy(buf);
    printf("%s\n",buf);
}

寿限無落語の文字列を全部丸々コピーするので処理時間が掛かります。


●pointer版は早いです

#include <stdio.h>
#include <string.h>
//長い寿限無落語
static char input[] = 
    "Jugemu Jugemu," 
    "Gokō no Surikire," 
    "Kaijarisuigyo no Suigyōmatsu," 
    "Ungyōmatsu," 
    "Fūraimatsu," 
    "Kuunerutokoro ni Sumutokoro," 
    "Yaburakōji no Burakōji," 
    "Paipo Paipo," 
    "Paipo no Shūringan," 
    "Shūringan no Gūrindai," 
    "Gūrindai no Ponpokopī no Ponpokona no Chōkyūmei,"
    "Chōkyūmei no Chōsuke";
void use_pointer(char **output)
{
    puts("■例えば:長い寿限無落語のCDの置き場所をコピーするのがポインタ板");
    printf("%sは%zu byte の転送命令が生成されます\n",__func__,sizeof(&input[0]));
    *output = &input[0];
}

int main(void){
    char    *pointer = NULL;
    use_pointer(&pointer);
    printf("%s\n",pointer);
}

寿限無落語の文字列のアドレスだけコピーするので効率が良く処理時間が早いです。
注:コンピュータは早いのでstrcpy版とpointer版の速さの違いはこのサンプルだけでは体感できません。