C言語関係掲示板

過去ログ

No.1225 const指定の配列の特定要素のみを変更可にする

[戻る] [ホームページ]
No.16178

配列の特定の要素のみを変更可にする
投稿者---RiSK(2004/08/14 23:39:18)


配列の特定の要素のみを変更可能にする方法はあるでしょうか?
それ以外の要素は const のようにしたいです。

以下は入力された日付の妥当性を確認するプログラムです。
#include <stdio.h>

#define IS_YEAR_VALID(year) (((year) >= 1) && ((year) <= 9999))
#define IS_MONTH_VALID(month) (((month) >= 1) && ((month) <= 12))
#define IS_LEAP_YEAR(year) ((((year) % 4) == 0) && (((year) % 100) != 0) || (((year) % 400) == 0))

typedef struct {
  int year;
  int month;
  int day;
} MY_DATE;

int IsDateValid(MY_DATE *date)
{
  /* const */ int days_a_month[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

  if (!IS_YEAR_VALID(date->year)) {
    return 0;
  }
  if (!IS_MONTH_VALID(date->month)) {
    return 0;
  }

  /*
   * 日を確認
   */

  // 閏年確認

  if (date->month == 2 && IS_LEAP_YEAR(date->year)) {
    days_a_month[2-1] = 29;
  }
    
  if ((date->day < 1) || (date->day > days_a_month[date->month-1])) {
    return 0;
  }

  return 1;
}

int main(void)
{
  MY_DATE date;

  scanf("%d %d %d", &date.year, &date.month, &date.day);

  if (IsDateValid(&date)) {
    puts("有効な日付");
  } else {
    puts("無効な日付");
  }

  return 0;
}
days_a_month は基本的に変更しないので const にしたいところですが、
閏年という例外があるので const にできません。
でも days_a_month[1] 以外の要素は変更する予定はありませんし、
うっかり変更してしまうミスも避けたいです。

もしあるならば
・コンパイラでうまく確認する方法
・それ以外で目的を達成する方法
を知りたいです。

どんなことでも構いません。よろしくお願いします。


No.16180

Re:配列の特定の要素のみを変更可にする
投稿者---かずま(2004/08/15 01:27:42)


> 配列の特定の要素のみを変更可能にする方法はあるでしょうか?
> それ以外の要素は const のようにしたいです。
キャストを使えばできます。

    *(int *)&days_a_month[2-1] = 29;


> どんなことでも構いません。よろしくお願いします。
 
const にしようがしまいが、days_a_month は自動変数ですから、
関数 IsDateValid が呼び出されるたびに、毎回毎回、12個の int の
初期化が実行されます。値の変化しない配列を利用したいのなら、
const よりも static を用いるべきです。

私なら次のようなコーディングをします。

int IsDateValid(const MY_DATE *date)
{
    static const int days_a_month[] = {
        31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
    };
    int last_day_of_a_month;

    if (!IS_YEAR_VALID(date->year)) return 0;
    if (!IS_MONTH_VALID(date->month)) return 0;
    if (date->month == 2 && IS_LEAP_YEAR(date->year))
        last_day_of_a_month = 29;
    else
        last_day_of_a_month = days_a_month[date->month - 1];
    if (date->day < 1 || date->day > last_day_of_a_month) return 0;
    return 1;
}

変数の書き換えを心配するのなら、引数の書き換えも心配すべきでしょう。
const MY_DATE *date のことです。



No.16182

Re:配列の特定の要素のみを変更可にする
投稿者---RiSK(2004/08/15 16:36:28)


かずまさん、レスありがとうございます。

> *(int *)&days_a_month[2-1] = 29;

あーなるほど、 const 外しですね。
ちょっと気持ち悪いので、今回は採用を見送りました。
こういう方法もあると覚えておきたいと思います。

> 私なら次のようなコーディングをします。

とても参考になりました。
別の変数を用意すれば問題ないわけですね。
# うぅ、頭かたいなぁ>私
今回はこの方法を用いました。解決です。


> 変数の書き換えを心配するのなら、引数の書き換えも心配すべきでしょう。
> const MY_DATE *date のことです。

仰るとおりです。訂正しました。
# 同一掲示板上で二回目の指摘を受けてしまった。
# 猛省します。


問題解決+αの知識をありがとうございました。


No.16183

Re:配列の特定の要素のみを変更可にする
投稿者---かずま(2004/08/15 22:16:52)


> 問題解決+αの知識をありがとうございました。

では、問題点をもう少し。

100で割り切れて 400で割り切れない年が平年というのはグレゴリオ暦で、
その暦が採用されたのはイタリアで 1582年ですから、それ以前の年について
IS_LEAP_YEAR を適用するのは無意味です。すなわち、IS_YEAR_VALID の
year >= 1 は変です。


No.16184

Re:配列の特定の要素のみを変更可にする
投稿者---RiSK(2004/08/15 23:02:13)


>では、問題点をもう少し。

問題点の指摘に感謝します。

>100で割り切れて 400で割り切れない年が平年というのはグレゴリオ暦で、
>その暦が採用されたのはイタリアで 1582年ですから、それ以前の年について
>IS_LEAP_YEAR を適用するのは無意味です。

なるほど。私も調べてみました。
グレゴリオ暦 - Wikipedia

>すなわち、IS_YEAR_VALID の year >= 1 は変です。

えーっと、要点ではないと思い書かなかったのですが
この問題には元ネタがあり、それは ここの過去ログ29 です。

> Y:西暦(0001-9999)

という指示だったので year >= 1 としていました。
「紀元前を含めないのはなぜか」と無知なりに考えていましたが、
私の思考はそこでストップしていました。

かずまさんがハッキリとした根拠を示してくださったので、
問題が悪かったことにし、 year >= 1582 とします。

# …でも、西暦が 9999 年までというのも変だよなぁ。


No.16186

Re:配列の特定の要素のみを変更可にする
投稿者---シャノン(2004/08/16 11:15:39)


#どうでもいいことなのかもしれませんが

>その暦が採用されたのはイタリアで 1582年ですから

そこまでこだわるのでしたら、月日もこだわるべきでは?
1582.01.01 に導入されたのではありませんし。
Wikipedia によれば、イタリアでは 1582.10.15 ですか。
ならば 1582.10.14 以前は無効な日付とすべきでは?

参考までに。
MFC の MonthCalendarCtrl で選択できる最古の日付は 1752.09.14。
1752.09.19 の次は 1752.10.01 でした。
C# の MonthCalendarCtrl では 1753.01.01 が最古でした。

MFC はイギリス歴、C# も基本的にはイギリス歴ですが、半端な 1752 年を含まない措置が取られているようですね。