Зачем возвращать статический указатель вместо параметра 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);

Мы всегда должны принимать решения во время разработки. Я просто спрашиваю, почему они решили возвращать статический указатель вместо того, чтобы принимать "переменную" в качестве параметра.

Кстати (это другой вопрос), почему они не размещают свой результат в куче? Это позволяет использовать что-либо вместо malloc или просто для эффективности?

Ответы

Ответ 1

Спецификация функций ctime и asctime восходит к C89, и в те времена все делалось немного по-другому, главным образом потому, что многопроцессорные системы были не очень распространены и, таким образом, использование статического буфера не вызывало бы больших проблем.

Скорее всего, они не возвращали динамически распределенную память, потому что это занимало дополнительное время, и в те дни циклы ЦП были труднее проходить.

Если вы работаете в системе POSIX, такой как Linux, у вас есть две другие доступные функции, которые в основном и являются альтернативой:

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

Эти функции получают указатель на буфер, который может получать выходные данные (и они возвращают указатель на тот же буфер). Суффикс _r означает "входящий", что означает, что его можно безопасно вызывать либо в многопоточной программе, либо несколько раз без точки последовательности между ними.

Ответ 2

Это означает, что я должен скопировать данные, которые я только что получил в результате

Зачем вам нужно копировать это?

Обратите внимание, что даже если вы скопируете данные, как только получите, вы все равно будете открыты для гонок.

Почему это было реализовано таким образом?

Потому что, когда они были стандартизированы (1989), большая часть программного обеспечения не была многопоточной, даже если многопоточность существовала со времен мэйнфреймов. Для справки, даже потоки POSIX были стандартизированы на несколько лет позже (1996), чем эти функции. Также не помогло то, что компьютеры становились все быстрее с каждым годом, а многоядерные /SMT-процессоры появлялись только с 2001 по 2006 год.

Если вам нужно что-то еще, вы всегда можете использовать системную функцию.

почему они не распределяют свой результат по куче?

Выделение очень дорого.

Это позволяет использовать что-либо вместо malloc или просто для эффективности?

Не уверен, что вы подразумеваете под этим. Правильный способ сделать это - передать указатель на буфер назначения, чтобы пользователь мог выбрать, какой метод выделения использовать.

Ответ 3

Вы (почти) описываете варианты _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]; это именно то, что нужно.

Ответ 4

C - продукт начала 1970-х, и это наследие проявляется в таких вещах. strtok также использует статический буфер и не является ни потокобезопасным, ни повторно входящим.

Я не видел однозначного объяснения, почему эти функции были реализованы таким образом. Возможно, это было для экономии места в стеке (в то время 128 кБ было очень дорогой памятью), возможно, для того, чтобы избежать проверки во время выполнения размера или допустимости целевого буфера и т.д. C изначально был разработан для системного программирования. Таким образом, если бы было много вычислений времени, я вижу, что этот подход экономит значительное количество циклов в течение дня.

К сожалению, это предположение с моей стороны. Я согласен, что прохождение целевого буфера является лучшим решением, и это должно быть путем вперед.