- PR -

C の localtime() の引数は混乱を招く。

投稿者投稿内容
未記入
会議室デビュー日: 2004/09/18
投稿数: 4
投稿日時: 2004-09-22 17:37
コブラさん

そうですね、CでC++を完全に模倣するのは難しいですけど、
「staticをつけた大域変数・大域関数はファイルスコープになる」
という性質を利用すれば、近いところまでいけるかもしれませんよ。
めげずに頑張って下さい。

蛇足ですが、ほむらさんの件は
コード:
typedef void (*FUNC_FOO)(char*);


ですね。
ほむら
ぬし
会議室デビュー日: 2003/02/28
投稿数: 583
お住まい・勤務地: 東京都
投稿日時: 2004-09-22 22:14
ども、ほむらです。
---------
未記入氏へ
引用:

蛇足ですが、ほむらさんの件は
コード:
typedef void (*FUNC_FOO)(char*);


ですね。


フォローサンクスです。
なんとなく違う気だけはしていたのですけどやっぱ間違えていましたかぁ^^;;;

コブラ氏へ
引用:

未記入氏: のサンプルは、これ、後スコープ演算子ついたらもう殆ど C++ と変わりないですな。
しかし、「継承」「多重継承」「インターフェース」とかは、やっぱ C++ ならでわ、
ですか・・・


多重継承は無理でしょうけど継承みたいなことならできますよ。

コード:
struct tagSuper {};
struct tagSub {
    struct tagSuper super;
    int x;
    int y
};


これつまりは継承なので(笑
基底の構造体を先頭に持ってくるのがポイントです。(実体)

# あくまでintel系の話なのでモトローダ系は反対になると思います

やっぱC言語ってメモリとの格闘だ(意味が違う笑
コブラ
ぬし
会議室デビュー日: 2003/07/18
投稿数: 1038
お住まい・勤務地: 神奈川
投稿日時: 2004-09-27 13:26
いやー、皆さん色々引き出しが多いですな・・・勉強さして貰ろてます。
ほむら氏のこれは、

struct tagSuper {};
struct tagSub {
struct tagSuper super;
int x;
int y
};


struct tagSuper {};
struct tagSub {
int x;
int y;
struct tagSuper super;
};

こうしないのは、コンパイラのパディングを予想してという理由だけなのでしょうか?
それとも他に何か私の思いもよらない理由があるのでしょうか?

後、また困ってます (笑)

コード:
#include <math.h>

#define PI  3.1415926575

int main()
{
    double c, r, f;

    printf("%4.8f, %4.8f, %4.8f\n", floor(PI), round(PI), ceil(PI));
    printf("%4.8f, %4.8f, %4.8f\n", floor(PI), ceil(PI), round(PI));
    printf("%4.8f, %4.8f, %4.8f\n", ceil(PI), floor(PI), round(PI));
    printf("%4.8f, %4.8f, %4.8f\n", ceil(PI), round(PI), floor(PI));
    printf("%4.8f, %4.8f, %4.8f\n", round(PI), floor(PI), ceil(PI));
    printf("%4.8f, %4.8f, %4.8f\n", round(PI), ceil(PI), floor(PI));
}



$ gcc -o pi -O pi.c -lm
$ ./pi
3.00000000, 0.00000000, 2.38821840
3.00000000, 4.00000000, 2.38821793
4.00000000, 3.00000000, 2.38821793
4.00000000, 0.00000000, 2.38821840
0.00000000, 0.00000000, 2.38821840
0.00000000, 0.00000000, 2.38821840
$

順番に依っても結果は変わってくるみたいですけど、ちょっと、酷くないすか?
これ(笑) round() が最悪。まぁ、printf で表示した時だけですけども。オカしく
なるのは。va_list には公平に値が渡ってるハズなんですが。。。

しかも、Solaris上 で

$man round

ってやっても「マニュアルには round のエントリがありません。」
これですわ・・・(プ

printf() の引数に ceil(), round(), floor() の内、どれか一個だけ指定
したら、何事も無く期待値が表示されるんですがねぇ〜
ナキヲ
常連さん
会議室デビュー日: 2003/08/22
投稿数: 32
お住まい・勤務地: 京都・自宅から勤務地まで自転車で40分
投稿日時: 2004-09-27 15:05
以下、私の環境(vine-3,gcc3.3.2)で確認した話です。

round()について
この関数は、C99で追加されたようで、
標準状態では、ヘッダで定義されない状態になっています。

-Wallオプションをつけてコンパイルすると、
implicit declaration of function `round'
という警告が出ます。
プロトタイプが無いため、round()がintを返すと判断されて、
結果がおかしくなっているのでしょう。

#define _ISOC99_SOURCE
#include <math.h>
とすると、
round()のプロトタイプが有効になって結果が正常になります。
このあたりのしかけは、features.hで行われています。

Solarisでも同じかどうかは不明です。

このあたり、C99の仕様に書いてあるんでしょうか。。。



[ メッセージ編集済み 編集者: ナキヲ 編集日時 2004-09-27 15:07 ]
コブラ
ぬし
会議室デビュー日: 2003/07/18
投稿数: 1038
お住まい・勤務地: 神奈川
投稿日時: 2004-09-27 17:15
>プロトタイプが無いため、round()がintを返すと判断されて、
>結果がおかしくなっているのでしょう。

>#define _ISOC99_SOURCE
>#include <math.h>
>とすると、
>round()のプロトタイプが有効になって結果が正常になります。
>このあたりのしかけは、features.hで行われています。

指摘の通りでした。
ミソは _ISOC99_SOURCE の #define でした。
RedHat9 の gcc で正しい結果が表示されました。

しかし、Solaris の gcc3.2 では、、ダメでした。。。
man にも無いし、やはり関数として存在しない様です。

しかし、Linux系でも round() を標準算術関数として公にしないのは何か
理由でもあるのでしょうかね。
ナキヲ
常連さん
会議室デビュー日: 2003/08/22
投稿数: 32
お住まい・勤務地: 京都・自宅から勤務地まで自転車で40分
投稿日時: 2004-09-27 17:36
引用:

しかし、Solaris の gcc3.2 では、、ダメでした。。。
man にも無いし、やはり関数として存在しない様です。


コンパイル・リンク・実行まで出来たのなら、
関数自体はあるんじゃないでしょうか。
引用:

しかし、Linux系でも round() を標準算術関数として公にしないのは何か
理由でもあるのでしょうかね。


さっきも書きましたが、C99という新しいC標準規格で追加されたものです。
標準状態を、旧仕様にするか新仕様にするかは
意見がわかれるところだと思いますが、
互換性重視で、旧仕様をデフォルトとしているのでしょう。

私の環境で、前の投稿に書いた、
features.hには
_ISOC9X_SOURCE
_ISOC99_SOURCE
と、二つのマクロがあります。
_ISOC9X_SOURCEの方が古いのかもしれません。
これまた歴史的経緯でしょうか。詳しい方教えてください。

一度、math.hとその中でincludeされているファイルを覗いて、
round()の定義まわりをみてみるといいんじゃないでしょうか。

結局ソースが一番信頼出来るドキュメントということで。。
ソース以外まともなドキュメントが無い場合もありますし。。

ところで、
round()のmanページにはC99で追加されたことしか
書いてありませんが、
_ISOC99_SOURCEのdefineは常識的事項なんでしょうか?
C99の追加仕様自体あんまり詳しくないのでなんともわかりません。
コブラ
ぬし
会議室デビュー日: 2003/07/18
投稿数: 1038
お住まい・勤務地: 神奈川
投稿日時: 2004-09-28 12:05
>コンパイル・リンク・実行まで出来たのなら、
>関数自体はあるんじゃないでしょうか。

いや、、一番最初に貼り付けた printf の6通りの結果は、RedHat9 上での結果です。
ちょっと、日本語を省略し過ぎました。。

どうやら

「しかも、Solaris上 で」

この表現がマズかったらしく、Solaris への言及が唐突過ぎるので、printf の結果
そのものも Solaris 絡みという判断もできてしまいますが、実は、前提は
RedHat9 で _ISOC99_SOURCE の #define 無しで出た round() の結果は散々・・・
で、最初の printf での 6通りの結果が "Linux Square" だからというので
Linux(RedHat) 上での結果であるというのを暗黙にしてしまったのがマズかった
様です。

正しくは、

「RedHat9 で斯様な惨憺たる実行結果を招き、しかも Solaris では RedHat9 に
存在しておる "man round" ってやっても「マニュアルには round のエントリがありません。」 ・・・・

と書かないと、正確な意味が伝わり難いかも知れませんな。
こういうとこから認識のギャップが・・・

っちゅぅこって、Solarisではコンパイルできなかったので、round() を外して
ceil() と floor() のみで実行してました。

ん〜、面倒臭がらず、背景を省略せずに書く事はこういう媒体では重要であると再認識
させられる顛末でした。

で、round() の部分的封印は、互換性の為でしたか。。。何らかの体系変更の過渡期には
こういう事もあるんですな。
コブラ
ぬし
会議室デビュー日: 2003/07/18
投稿数: 1038
お住まい・勤務地: 神奈川
投稿日時: 2004-09-29 13:09
前に、誰かに「標準関数が気に食わんのなら自分用の関数作ったらエエ」とか言われましたが、
全くその通りやと思いました。で、localtime(); の引数を間違え易いので、自分で
localtime() そっくりの結果を返す localtime_s() っちゅぅ関数を作りました。

コード:
#include <stdlib.h>
#include <math.h>
#include <time.h>
#include <sys/time.h>

#define	_ISOC99_SOURCE

#define DAYS	(60 * 60 * 24)
#define NORM	(DAYS * 365)
#define LEAP	(DAYS * 366)
#define EPOC	1970

typedef unsigned int bool;

static const bool TRUE = 1;
static const bool FALSE= 0;

short ttl[13] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365};
short mon[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
enum date {Thu, Fri, Sat, Sun, Mon, Tue, Wed};

///////////////////////////////////////////////////////////////////
// localtime_s()
// 機能    : 年・月・日・曜日・時・分・秒・通算経過日数・夏時間を出力
//
// 入力    :
//     第一引数: *tm
//           型: tm 構造体へのポインター   
//     第二引数: t
//           型: unsigned long
//
// 出力    :     *tm 
//           型: tm 構造体へのポインター
//
// 戻り値  : 論理型
//           TRUE : 1
//           FALSE: 0
//
///////////////////////////////////////////////////////////////////
bool localtime_s(struct tm *tm, time_t t)
{
    enum date wday = Thu;
    time_t now;
    unsigned long tmp = 0L, temp=0L;
    unsigned int y, month, h, mn, s, c, zg, cur;
    double d, days, gap, last;
    unsigned int leap = 0;
    short i=0;
    char *TZ, tz[4];
    char flg = '0';

    /* 初期化 */
    y = month = h = mn = s = c = zg = cur = 0;
    d = days = 0.0000;

    /* 1970年1月1日から指定年度まで */
    for(tmp = 0; tmp <= t; tmp += NORM){
        c = y + EPOC;

        /* 指定時刻の前年度迄でループを止める */
        if(tmp + NORM >= t){
            break;
        }

        /* 閏年カウント */
        if(!(c % 4) && ((c % 100) || !(c % 400))){
            leap++;
        }

        y++;
    }

    y = c;
    /* 指定年度分のみの秒数 */
    last = (double)t - tmp;

    /* 閏年の分を引く */
    gap = (double)(last - (leap * DAYS));

    /* 1970年1月1日は木曜日(wday[0..3] スキップ)開始 */
    temp = Sun;
    temp += t / DAYS;
    if(!(y % 4) && ((y % 100) || !(y % 400))) temp++;
    /* 1週間で割った余りの曜日 */
    temp %= 7;

    if(gap > 0) days = gap / DAYS;
    else days = 1;

    for(i = 1; i <= 12; i++){
        /* 指定年度分の総秒数が1月以降かどうか */
        if(gap > (ttl[1] * DAYS)){
            /* 月毎の累積秒数が指定秒数を上回らないように */
            if(gap <= (ttl[i + 1] * DAYS)){
                /* 先月に一月足したら今月 */
                month = i + 1;
                /* 指定秒数を日数に変換、前月までの累積日数
                   を引いたら今月分の日付 */
                d = (double)(gap / DAYS) - ttl[i];

                /* 0日は1日 */
                if(d == 0.000) d = 1.000;

                break;
            }
        }
        /* 年度が変わって一ヶ月経ってない */
        else {
            /* 31日以内では差分日数がそのまま日付になる */
            if(gap > 0) d = (double)(gap / DAYS);
            else d = 1.000; // 0日は1日
            /* 今月は 1月確定 */
            month = i;

            break;
        }
    } 

    /* 指定時刻時・分・秒の割出し */
    h  = t % DAYS;
    mn = h % (60 * 60);
    s  = mn % 60;

    h  /= (60 * 60);
    mn /= 60;

    /* タイムゾーン増減 */
    TZ = (char *)getenv("TZ");
    memset(tz, 0x00, sizeof(tz));
    strncpy(tz, (TZ + 3), 3);

    /* atoi() */
    for(i=1, zg=0; i <= strlen(&tz[1]); i++){
        zg *= 10;
        zg += (tz[i] - 0x30);
    }

    /* ローカル化して24時間を超えるようなら、
       日付、累積日数、曜日全てが増分 */
    if(h + zg >= 24){
        d = d + 1.000;
        /* 月末調整 */
//        if(d > mon[month]){
//            month++;
//            d = 1.000;
//        }

        days++;
        temp++;
    }

    /* これは不必要な処理 */
    time(&now);
    cur = now / NORM;
    cur += 1970;

    /* ローカライズタイム */
    if(cur == y) h += zg;

    /* 24時間サイクル化 */
    if(h >= 24){
        h %= 24;
    }

    wday = temp;

    /* 第一引数構造体へ代入 */
    tm->tm_sec = s;
    tm->tm_min = mn;
    tm->tm_hour = h;
    tm->tm_mday = (int)d;
    tm->tm_mon = month;
    tm->tm_year = y;
    tm->tm_wday = wday;
    tm->tm_yday = (int)days;
    tm->tm_isdst = 0;

    /* 成功 */
    return(TRUE);
}



お気づきとは思いますが、第一引数は tm 構造体の「実体の」アドレス、
第二引数は time_t 型の実値が入ります。

、、ぶっちゃけ曜日が一日狂う時が「たまに」あるんですが (プ
今、原因を調べてます。

使用例:
コード:
int main()
{
    time_t t;
    struct tm tm;
    static unsigned char *date[] = {"日", "月", "火", "水", "木", "金", "土" };
    unsigned char buf[7];
    char *Z;

    time(&t);
/*    localtime_s(&tm, 31536000L); */
    localtime_s(&tm, t);

    Z = (char *)getenv("TZ");
    memset(buf, 0x00, sizeof(buf));
    strncpy(buf, Z, 3);

    printf("%d年  ", tm.tm_year);
    printf("%d月 ", tm.tm_mon);
    printf("%d日 ", tm.tm_mday);
    printf("%s曜日 ", date[tm.tm_wday]);
    printf("%02d:", tm.tm_hour);
    printf("%02d:", tm.tm_min);
    printf("%02d", tm.tm_sec);
    printf(" %d日目 ", tm.tm_yday);

    printf("%d ", tm.tm_isdst);
    printf("%s\n", buf);

    exit(0);
}



こんな感じで・・・

スキルアップ/キャリアアップ(JOB@IT)