web-dev-qa-db-ja.com

なぜoutパラメータの代わりに静的ポインタを返すのですか?

char* asctime (const struct tm * timeptr);
char* ctime (const time_t * timer);

time.h内の多くの関数が静的変数へのポインターを返すことがわかりました。これは、それらの関数への後続の呼び出しによって変更される可能性があります。つまり、結果として取得したばかりのデータをコピーする必要があります。これは、実行する必要のある追加の操作であり、これらの関数がスレッドセーフではなくなります。

なぜそれがそのように実装されたのですか?これらの署名の方が優れているのではないでしょうか?

void asctime (char * out, const struct tm * timeptr);
void ctime (char * out, const time_t * timer);

私たちは常に開発中に決定を下さなければなりません。 「out変数」をパラメーターとしてとるのではなく、なぜ静的ポインターを返すことを選んだのかを尋ねています。

ちなみに(これは別の質問です)、結果をヒープに割り当てませんか?それはmallocの代わりに何かを使用できるようにするのですか、それとも単に効率を上げるためですか?

ctime関数とasctime関数の仕様はC89にまで遡ります。主にマルチプロセッサシステムがあまり一般的ではなかったため、静的バッファは大きな問題を引き起こしません。

ほとんどの場合、余分に時間がかかったため動的に割り当てられたメモリを返さず、当時はCPUサイクルを取得するのが困難でした。

LinuxのようなPOSIXシステムを使用している場合は、他に2つの機能が利用できます。これらは基本的に代替として説明した機能です。

   char *asctime_r(const struct tm *tm, char *buf);
   char *ctime_r(const time_t *timep, char *buf);

これらの関数は、出力を受け取ることができるバッファーへのポインターを受け取ります(そして、同じバッファーへのポインターを返します)。 _r接尾辞は「再入可能」を意味します。つまり、マルチスレッドプログラムで、またはその間にシーケンスポイントがなくても複数回安全に呼び出すことができます。

22
dbush

つまり、結果として取得したばかりのデータをコピーする必要があります

なぜそれをコピーする必要があるのですか?

データを取得したらすぐにコピーしても、レースに参加できることに注意してください。

なぜそれがそのように実装されたのですか?

標準化されたとき( 1989 )、メインフレームの時代からマルチスレッドが存在していても、ほとんどのソフトウェアはマルチスレッドではありませんでした。参考までに、POSIXスレッドでさえ、これらの関数より数年後( 1996 )に標準化されました。また、コンピュータが1年ごとに速くなり、マルチコア/ SMTプロセッサが 2001 - 2006 になるまで登場しなかったことも役に立ちませんでした。

他に何か必要な場合は、常にシステム固有の関数を使用できます。

結果をヒープに割り当てないのはなぜですか?

割り当ては非常に高価です。

それはmallocの代わりに何かを使用できるようにするのですか、それとも単に効率を上げるためですか?

その意味がわからない。これを行う適切な方法は、ユーザーが使用する割り当て方法を選択できるように、宛先バッファーにポインターを渡すことです。

12
Acorn

あなたは(ほとんど)_s C11で追加されたバリアント

errno_t ctime_s(char *buffer, rsize_t bufsz, const time_t *time);
errno_t asctime_s(char *buf, rsize_t bufsz, const struct tm *time_ptr);

十分な大きさがある場合、これらは指定された場所に書き込み、それ以外の場合はエラーを報告します。

ご存知のように、これらの呼び出しにバッファをmallocする必要はありませんchar buf[26];はまさに必要なものです。

6
Caleth

Cは1970年代初頭の製品であり、その遺産はこのようなものに現れています。 strtokも静的バッファを使用し、スレッドセーフでも再入可能でもありません。

これらの機能がそのように実装された理由についての明確な説明は見ていません。スタックスペースを節約した可能性があります(128 kBは当時は非常に高価なメモリでした)、ターゲットバッファーのサイズや有効性のランタイムチェックを回避する必要があった可能性があります。Cはもともとシステムプログラミング用に設計されましたなので、時間の計算が頻繁に行われている場合、このアプローチにより、1日のサイクル数を大幅に節約できることがわかります。

残念ながら、それは私の側の推測です。ターゲットバッファーを渡す方がより良い解決策であり、それが先のパスであることに同意します。

5
John Bode