web-dev-qa-db-fra.com

Pourquoi renvoyer un pointeur statique au lieu d'un paramètre out?

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

J'ai trouvé que de nombreuses fonctions à l'intérieur de time.h renvoie des pointeurs vers des variables statiques, qui pourraient être modifiées par tout appel ultérieur à ces fonctions. Cela signifie que je dois copier les données que je viens d'obtenir et c'est une opération supplémentaire que je dois exécuter et qui rend ces fonctions non sécuritaires pour les threads.

Pourquoi a-t-il été mis en œuvre de cette façon? Ces signatures ne seraient-elles pas meilleures?

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

Nous devons toujours prendre des décisions pendant le développement. Je demande juste pourquoi ils ont choisi de renvoyer un pointeur statique au lieu de prendre une "variable out" comme paramètre.

Au fait (c'est une autre question), pourquoi n'allouent-ils pas leur résultat sur le tas? Est-ce pour permettre l'utilisation de quoi que ce soit au lieu de malloc ou simplement pour l'efficacité?

17

La spécification des fonctions ctime et asctime remonte à C89, et les choses étaient un peu différentes à l'époque, principalement parce que les systèmes multiprocesseurs n'étaient pas très courants et le tampon statique ne causerait pas de gros problème.

Très probablement, ils n'ont pas renvoyé de mémoire allouée dynamiquement car cela prenait plus de temps, et à cette époque, les cycles CPU étaient plus difficiles à trouver.

Si vous êtes sur un système POSIX comme Linux, vous avez deux autres fonctions disponibles qui sont essentiellement ce que vous avez décrit comme une alternative:

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

Ces fonctions prennent un pointeur sur un tampon qui peut recevoir la sortie (et elles renvoient un pointeur sur ce même tampon). Le _r suffixe signifie "réentrant", ce qui signifie qu'il peut être appelé en toute sécurité soit dans un programme multithread, soit plusieurs fois sans point de séquence entre les deux.

22
dbush

Cela signifie que je dois copier les données que je viens d'obtenir

Pourquoi avez-vous besoin de le copier?

Notez que même si vous copiez les données dès que vous les obtenez, vous serez toujours ouvert aux courses.

Pourquoi a-t-il été mis en œuvre de cette façon?

Parce que lorsqu'ils ont été standardisés ( 1989 ), la plupart des logiciels n'étaient pas multi-thread même si le multi-thread existait depuis l'ère du mainframe. Pour référence, même les threads POSIX ont été standardisés des années plus tard ( 1996 ) que ces fonctions. Cela n'a pas aidé non plus que les ordinateurs soient devenus plus rapides chaque année et que les processeurs multicœurs/SMT n'apparaissent pas avant 2001 - 2006 .

Si vous aviez besoin d'autre chose, vous pouvez toujours utiliser une fonction spécifique au système.

pourquoi ne répartissent-ils pas leur résultat sur le tas?

L'allocation est très coûteuse.

Est-ce pour permettre l'utilisation de quoi que ce soit au lieu de malloc ou simplement pour l'efficacité?

Je ne sais pas ce que tu veux dire par là. La bonne façon de procéder consiste à passer un pointeur vers le tampon de destination, afin que l'utilisateur choisisse la méthode d'allocation à utiliser.

12
Acorn

Vous décrivez (presque) le _s variantes ajoutées 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);

Ceux-ci écrivent à l'emplacement spécifié, à condition qu'il soit suffisamment grand, et signalent l'erreur sinon.

Vous n'avez pas besoin de malloc les tampons pour ces appels, comme vous le savez char buf[26]; est exactement ce qu'il faut.

6
Caleth

C est un produit du début des années 1970, et cet héritage se manifeste dans des choses comme celle-ci. strtok utilise également un tampon statique et n'est ni thread-safe ni rentrant.

Je n'ai pas vu d'explication définitive pour expliquer pourquoi ces fonctions ont été implémentées de cette façon. Il s'agissait peut-être d'économiser de l'espace sur la pile (128 Ko, c'était beaucoup de mémoire très coûteuse à l'époque), il s'agissait peut-être d'éviter les vérifications d'exécution de la taille ou de la validité du tampon cible, etc. C a été initialement conçu pour la programmation des systèmes , donc si les calculs de temps étaient beaucoup effectués, je peux voir que cette approche permet d'économiser un nombre important de cycles au cours de la journée.

Malheureusement, c'est de la spéculation de ma part. Je suis d'accord que passer le tampon cible est la meilleure solution, et cela devrait être la voie à suivre.

5
John Bode