fonctions retournant le pointeur de caractère

Je suis tombé sur de nombreuses fonctions renvoyant des pointeurs de caractères dans une application héritée. Certains d’entre eux renvoient des pointeurs vers des tableaux de caractères locaux. Cela semble provoquer des plantages après plusieurs appels (pas immédiatement!). Voir les utilisations ci-dessous.

char *f1(){ char buff[20]; char *ptr; ---- ---- ptr=buff; return ptr; } --- --- f2(f1()); 

f1 () retourne une variable locale de pointeur et la transmet ensuite à une autre fonction. J’ai eu le crash directement quand il est compilé en utilisant le mode _DEBUG dans MS DEV. Mais en mode de libération, cela ne provoque pas de crash immédiat, mais peut se produire après de nombreux appels de ce type.

Lorsque j’ai modifié l’utilisation ci-dessous, cela fonctionne sans problème. L’utilisation suivante est-elle sûre?

 strcpy(arr,f1()); /* arr is fixed char array*/ f2(arr); 

Non, c’est un comportement indéfini. Il se trouve que cela fonctionne dans votre cas, mais peut cesser de fonctionner à tout moment.

Non, ce n’est pas sûr. Le simple fait d’appeler strcpy peut modifier suffisamment la stack pour causer des problèmes ultérieurement, car l’adresse de retour et les parameters risquent d’écraser le tableau.

les solutions malloc sont intéressantes sauf que la mémoire doit être libre après utilisation. (en dehors de la fonction). Sinon, il y aurait une fuite de mémoire.

La fonction f1 renvoie un temporaire (buff) qui est libéré lors du retour de la fonction. Vous devez utiliser malloc () dans la fonction.

Ne jamais renvoyer un pointeur sur une variable locale. Cela peut fonctionner dans certaines situations, mais en général, vous aurez beaucoup de problèmes avec cela. Au lieu:

  • Indiquez à votre fonction qu’elle renvoie un pointeur sur la mémoire allouée et que le tampon renvoyé doit être libéré par l’appelant.
  • ajoutez un tampon et un argument de taille, et remplissez le tampon (c’est ce qui est généralement fait dans l’API Win32)
  • si vous utilisez C ++, utilisez std :: ssortingng

L’utilisation suivante est-elle sûre?

Non.

Si votre fonction renvoie un pointeur sur quelque chose, assurez-vous qu’elle alloue une zone mémoire et renvoie le pointeur sur celle-ci:

 char *safe_f1(void) { char *ptr; ptr = (char *) malloc(20 * sizeof(char)); ... return ptr; } 

Ce n’est pas sécuritaire. La raison est simple:

Toute variable d’une fonction sera allouée sur la stack dont la mémoire est libérée après le retour de la fonction. Le fait que la mémoire soit libérée ne signifie pas que son contenu est modifié.

Cela signifie que le contenu de la mémoire que vous avez mis dans la variable char buff[20] est toujours à la position de mémoire ptr ou ptr (puisque ptr=buff ). Chaque fois que vous appelez une autre fonction (ou exécutez un autre bloc), ses variables de fonction / bloc iront également dans la stack, ce qui permet de modifier le contenu de la mémoire pour laquelle pointe la position ptr .

Dans l’exemple strcpy vous avez écrit, vous avez eu la chance que les variables de la fonction strcpy ne soient pas sur la stack à un emplacement situé dans l’ancien tableau de buff . C’est la raison pour laquelle vous avez eu l’impression que c’était en sécurité.

La conclusion est qu’il n’ya aucun moyen de garantir qu’un contenu de mémoire libéré de la stack ne changera pas entre deux appels de fonction.

La solution consiste à utiliser malloc car malloc n’alloue pas de mémoire sur la stack, mais sur le tas. La mémoire de tas n’est pas désallouée à moins que vous n’ayez choisi de le faire (par un appel gratuit).

Cette approche garantirait que la mémoire pointée par ptr peut être utilisée en toute sécurité par toute autre fonction.

L’inconvénient de cette solution est insortingnsèque: une fois la mémoire libérée, à moins que vous ne le fassiez par programme, si vous oubliez de libérer cette mémoire et perdez le contenu de ptr , cette mémoire sera là, allouée pour votre programme mais jamais réalisable, perdue pour. aussi longtemps que votre programme s’exécute. Cette mémoire deviendra une fuite de mémoire 🙂

C’est l’une des raisons pour lesquelles certaines langues ont des éboueurs … mais c’est une autre histoire 🙂

PS: Je pense qu’il est prudent (si votre programme est à thread unique), bien que je ne recommande pas, de faire quelque chose comme ça:

 { char safe_buffer[20]; char *unsafe_ptr; int i; unsafe_ptr = f1(); /*Copy the buffer without calling any function not to change the stack content */ for(i=0;i<20 && *(unsafe_ptr + i) != 0;i++) { *(safe_buffer + i) = *(unsafe_ptr + i); } *(safe_buffer + i) = 0; f2(safe_buffer); } 

No..si toujours pas en sécurité.

Au moment où vous faites strcpy(arr,f1()); , le pointeur utilisé comme 2ème argument pointe déjà vers un tableau qui n’existe pas.

Non, vous voyez que le buff[20] n’est disponible que dans la fonction f1 . Pour être précis, la mémoire est allouée sur la stack f1 .

Vous devez créer un nouveau buff[20] sur le tas en utilisant malloc et renvoyer un pointeur sur cette mémoire à partir de f1 . Une autre méthode consiste à créer un buff[20] dehors de f1 (à partir de la fonction qui appelle f1 ) et à l’envoyer en tant qu’argument à f1 . f1 peut alors changer le contenu du tampon et n’a même pas à le renvoyer.

En fait, le mieux serait de modifier f1 () pour utiliser malloc (). Votre solution n’est pas proche d’un comportement défini.

Je suggérerais deux solutions possibles:

  1. Utilisez un char buff[20] statique char buff[20] dans f1 sauf si la fonction est appelée à partir de plusieurs threads ou si le monde extérieur enregistre le pointeur au-delà de strcpy.

  2. Utilisez return strdup (ptr); et free le pointeur en dehors de f1 . Ceci est plus facile à utiliser que malloc (bien que techniquement identique). Il est plus lent que 1. mais thread-safe.

Je suggère de changer ces fonctions pour prendre un pointeur qu’il utilise

 void f1(char *) 

Ainsi, chaque élément de code appelant la fonction doit décider de l’emplacement d’écriture de la mémoire et supprimer toute mémoire allouée.