忍者ブログ

Azukish

消えゆく世界と流れる未来に最後の灯を since 2006/4/3

2024/04/27

[PR]

×

[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。

コメント

ただいまコメントを受けつけておりません。

2015/06/15

C言語におけるlocaltime()関数の変な挙動

time()関数は実行した時の基準時間(1970年1月1日0時0分0秒)からの経過秒数をtime_t型(64bitのint?)で返す関数。
localtime()関数はtime()関数によって取得した時間をその場所の時間に変換して各時分秒を分けてtm構造体というやつに変数に代入する便利な関数。
tm構造体は、
struct tm {
    int tm_sec;        /* 秒 (0-60) */
    int tm_min;        /* 分 (0-59) */
    int tm_hour;       /* 時間 (0-23) */
    int tm_mday;       /* 月内の日付 (1-31) */
    int tm_mon;        /* 月 (0-11) */
    int tm_year;       /* 年 - 1900 */
    int tm_wday;       /* 曜日 (0-6, 日曜 = 0) */
    int tm_yday;       /* 年内通算日 (0-365, 1 月 1 日 = 0) */
    int tm_isdst;      /* 夏時間 */
};
となっている模様(Man page of CTIMEより)
と、いうふうに頭の中で解釈してる。まあ、偉い人、頭のいい人の解釈は違うかも知れないのでググッてくださいまし。
これらの関数は、C言語の標準ライブラリ関数に含まれていて、time.hをインクルードすれば使えるようになる。
で、今回問題として躓いたのは、このlocaltime()関数の戻り値がどうやら静的な領域に確保されるらしく、2つ以上tm構造体を宣言すると色々とアレなことが起こる模様、ということ。
localtime()は同じポインタを見ている - ソフトウェアエンジニア現役続行を参考にしました。

まず、これらを使って適当なコードを書いてLinux上で実行してみる。
#include <stdio.h>
#include <unistd.h>
#include <time.h>

int main() {
  time_t t;
  struct tm *t_st;
  for (int i=0; i<3; i++) {
    t=time(NULL);
    t_st=localtime(&t);
    printf("%d %d\n",t,t_st->tm_sec);
    sleep(1);
  }
}
こんな感じ。
$ gcc a.c -std=c11
$ ./a.out
1434360656 56
1434360657 57
1434360658 58

次に、tm構造体を持つ変数を2つ宣言・定義して実行してみる。
#include <stdio.h>
#include <unistd.h>
#include <time.h>

int main() {
  time_t t,t2;
  struct tm *t_st,*t_st2;
  t=time(NULL);
  t2=time(NULL);
  t_st2=localtime(&t2);
  for (int i=0; i<3; i++) {
    t=time(NULL);
    t_st=localtime(&t);
    printf("%d %d %d\n",t,t_st->tm_sec,t_st2->tm_sec);
    sleep(1);
  }
}
するとこんな感じ。
$ gcc a.c -std=c11
$ ./a.out
1434360821 41 41
1434360822 42 42
1434360823 43 43
t_st2はt2のアドレスを読んでるはずなのに、tの中身に従って変化しているのがわかる。
この変な仕様のせいでかなり時間を取られて悔しいです・・・。
ついでアドレスを読むと言っても、time()で取得した時刻の変化を読み取るためには、time()を実行した都度localtime()を実行しないといけないってのも、ちょっと意味不明な感じがするんだけど、これってどうしてなの?教えて偉い人。

ちなみに、同じような下記のプログラムをWindows上で、cl(Visual Studio 2012)でコンパイルして実行してみた。
#include <stdio.h>
#include <windows.h>
#include <time.h>

int main() {
  time_t t,t2;
  struct tm *t_st,*t_st2;
  int i;
  t=time(NULL);
  t2=time(NULL);
  t_st2=localtime(&t2);
  for (i=0; i<3; i++) {
    t=time(NULL);
    t_st=localtime(&t);
    printf("%d %d %d\n",t,t_st->tm_sec,t_st2->tm_sec);
    Sleep(1000);
  }
}
こんな感じで出力される。
> cl a.c
Microsoft(R) C/C++ Optimizing Compiler Version 17.00.61030 for x86
Copyright (C) Microsoft Corporation.  All rights reserved.

a.c
c:\program files (x86)\microsoft sdks\windows\v7.1a\include\sal_supp.h(57) : warning C4005: '__useHeader' : マクロが再定義されました。
        C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\INCLUDE\sal.h(2872) : '__useHeader' の前の定義を確認してください
c:\program files (x86)\microsoft sdks\windows\v7.1a\include\specstrings_supp.h(77) : warning C4005: '__on_failure' : マクロが再定義されました。
        C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\INCLUDE\sal.h(2882) : '__on_failure' の前の定義を確認してください
Microsoft (R) Incremental Linker Version 11.00.61030.0
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:a.exe
a.obj

> a.exe
1434362419 0 19
1434362420 0 20
1434362421 0 21
おいおい、実装がまるで違うじゃねえか!

2つのtm構造体が宣言されたとき、GCCでは同じアドレスを参照して、VSでは片方の情報が0になるっぽい?
って感じですかね。
てか、VSの挙動はどうみてもおかしいだろ・・・。さすがM$やで

拍手

コメント













カレンダー

03 2024/04 05
S M T W T F S
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30

アーカイブ

AD

Azkishはamazon.co.jpを宣伝しリンクすることによって サイトが紹介料を獲得できる手段を提供することを目的に設定されたアフィリエイト宣伝プログラムである、 Amazonアソシエイト・プログラムの参加者です。