¿Por qué devolver un puntero estático en lugar de un parámetro de salida?
time.h (4)
Eso significa que tengo que copiar los datos que acabo de obtener
¿Por qué necesitas copiarlo?
Tenga en cuenta que incluso si copia los datos tan pronto como los obtiene, seguirá abierto a las carreras.
¿Por qué se implementó de esa manera?
Porque cuando se estandarizaron ( 1989 ), la mayoría del software no era multiproceso, incluso si existía multihilo desde la era del mainframe. Para referencia, incluso las hebras POSIX se estandarizaron años más tarde ( 1996 ) que estas funciones. Tampoco ayudó que las computadoras se volvieran más rápidas cada año y que los procesadores multi-core / SMT no aparecieran hasta 2001 .
Si necesita algo más, siempre puede usar una función específica del sistema.
¿Por qué no asignan su resultado en el montón?
La asignación es muy costosa.
¿Es para permitir el uso de cualquier cosa en lugar de malloc o solo para la eficiencia?
No estoy seguro de lo que quieres decir con eso. La forma correcta de hacer esto es pasar un puntero al búfer de destino, de modo que el usuario elija qué método de asignación usar.
char* asctime (const struct tm * timeptr);
char* ctime (const time_t * timer);
Encontré que muchas funciones dentro de
time.h
devuelven los punteros a las variables estáticas, que podrían ser alteradas por cualquier llamada posterior a esas funciones.
Eso significa que tengo que copiar los datos que obtuve como resultado y es una operación adicional que debo ejecutar y eso hace que esas funciones no sean seguras.
¿Por qué se implementó de esa manera? ¿No serían estas firmas mejores?
void asctime (char * out, const struct tm * timeptr);
void ctime (char * out, const time_t * timer);
Siempre tenemos que tomar decisiones durante el desarrollo. Solo pregunto por qué eligieron devolver un puntero estático en lugar de tomar una "variable de salida" como parámetro.
Por cierto (esta es otra pregunta), ¿por qué no asignan su resultado en el montón? ¿Es para permitir el uso de cualquier cosa en lugar de malloc o solo para la eficiencia?
C es un producto de principios de la década de 1970, y ese legado se muestra en cosas como esta.
strtok
también utiliza un búfer estático y no es seguro para subprocesos ni reingresante.
No he visto una explicación definitiva de por qué esas funciones se implementaron de esa manera. Puede haber sido para ahorrar espacio de pila (128 kB era una gran cantidad de memoria muy costosa en ese momento), puede haber sido para evitar verificaciones de tiempo de ejecución en el tamaño o la validez del búfer de destino, etc. C fue diseñado originalmente para la programación de sistemas Entonces, si los cálculos de tiempo se hicieran mucho, puedo ver este enfoque ahorrando una cantidad significativa de ciclos a lo largo del día.
Lamentablemente, eso es especulación de mi parte. Estoy de acuerdo en que pasar el búfer de destino es la mejor solución, y ese debería ser el camino a seguir.
Está (casi) describiendo las variantes
_s
que se agregaron en 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);
Estos escriben en la ubicación especificada, siempre que sea lo suficientemente grande, e informen el error de lo contrario.
No es necesario que
malloc
los buffers para estas llamadas, como saben
char buf[26];
Es exactamente lo que se necesita.
La especificación de las funciones
asctime
y
asctime
se remonta al C89, y las cosas se hicieron un poco diferente en aquellos días, principalmente porque los sistemas multiprocesadores no eran muy comunes y, por lo tanto, el uso de un búfer estático no causaría un gran problema.
Lo más probable es que no devolvieron la memoria asignada dinámicamente porque tomó más tiempo, y en esos días era más difícil conseguir ciclos de CPU.
Si está en un sistema POSIX como Linux, tiene otras dos funciones disponibles que son básicamente lo que describió como una alternativa:
char *asctime_r(const struct tm *tm, char *buf);
char *ctime_r(const time_t *timep, char *buf);
Estas funciones llevan un puntero a un búfer que puede recibir la salida (y devuelven un puntero a ese mismo búfer).
El sufijo
_r
significa "reentrante", lo que significa que puede llamarse de forma segura en un programa multiproceso o más de una vez sin un punto de secuencia entre ellos.