The Man Who Fell From The Wrong Side Of The Sky:2010年8月17日分

2010/8/17(Tue)

[C language][Ancient UNIX] before time_t

ヒウイッヒー(死語)というサイトで

なぜ localtime(3) には、ポインタを渡すのか?

ちうネタが流れてたので。

	struct tm *
	localtime(const time_t *clock);

よくある誤回答の例としては

time_tが構造体かもしれない + 構造体の値渡し回避


ですが、そもそも ISO-C では time_t は算術型 (Arithmetic) と決められとるので
time_t が構造体だったらを考慮してポインタ渡し〜ということはありえません。
もし過去にそういう実装が存在したとしたら、ちゃんとISO-Cもそれを考慮し
time_t は実装依存の opaque 型と定めるでしょう、それくらい互換性はケアされとり。

さて、どう頭捻っても「意味が無い」という答えしか出てきませんね。
考えてわからないならソース考古学のお時間です。Cのバヤイ
どんなに理不尽と思える仕様があっても、何らかの歴史的理由があることがほとんどでし。

んでわ Ancient UNIX のマニュアル読んでみましょ、まずは UNIX 1st Editionから。
すでに time(2)ctime(3)が存在します。
この最初期の、まだアセンブラで書かている UNIX のプラットフォームは 18bit の PDP-7 です。

 DESCRIPTION   time returns the time since 00:00:00, Jan. 1, 1971, measured in
               sixtieths of a second. The high order word is in the AC register and
               the low order is in the MQ.


18bit ぽっちじゃ3〜4日で尽きてしまうので AC - MQ という2つのレジスタを使った
36bit で epoch(00:00:00, Jan. 1, 1971) からの秒数を保持しているわけです。

そして UNIX は移植性を求めて、プログラミング幻語Cで書き直されることになります。
その作業があらかた終わった 4th Editionをみてみましょ。

この頃には ctime(3) に加えて、お題の localtime(3)も追加されています。

SYNOPSIS
	char *ctime(tvec)
	int tvec[2];
	...
	int *localtime(tvec)
	int tvec[2];

この頃のプラットフォームも PDP-11 のような 16bit なんかが主力ですので
1st Editionと同様に上位ビットと下位ビットを配列で渡す必要があるわけです。

これ K&R なので若者には見慣れない構文かもしれませんが、ISO-C だと

SYNOPSIS
	char *ctime(int tvec[2])
	...
	int *localtime(int tvec[2]);

ですし、仮引数では配列型とポインタ型は区別がにゃいので

	char *ctime(int *tvec)
	...
	int *localtime(int *tvec);

と書き直せます、おおいっきにクロマニヨンに進化した!

つまり ctime(3) や localtime(3) の引数がポインタ型を要求するのは
この時代の名残なのですよな、後の 7th Edition では

SYNOPSIS
	char *ctime(clock)
	long *clock;

	...

	#include <time.h>

	struct tm *localtime(clock)
	long *clock;

とtime.h と struct tm が導入され、現在のプロトタイプと異なるのは
まだ typedef long time_t が入ってないというだけの状態になります。

time_tが登場するのは 4.3BSD Tahoeあたりですかね。
単純に s/long/time_t/ されたまま現在に至ると、ちとSVR4の方は資料今手元にないので以下略。