Зачем возвращать статический указатель вместо параметра out? - PullRequest
17 голосов
/ 09 июля 2019
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 или просто для эффективности?

Ответы [ 4 ]

22 голосов
/ 09 июля 2019

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

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

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

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

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

12 голосов
/ 09 июля 2019

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

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

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

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

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

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

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

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

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

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

6 голосов
/ 09 июля 2019

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

5 голосов
/ 09 июля 2019

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

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

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

...