Comment vérifier si un caractère * pointe sur un littéral de chaîne en C

J’ai une struct

struct request { int code; char *message; }; 

que je voudrais libérer correctement.

J’ai la fonction suivante pour le faire:

 void free_request(struct request *req) { if (req->message != NULL) { free(req->message); } free(req); req = NULL; } 

Le problème est que je reçois une erreur “free (): invalid pointer” / segfault du compilateur lorsque je tente de libérer une demande créée à l’aide d’un littéral de chaîne:

 struct request *req; req = malloc(sizeof(struct request)); req->message = "TEST"; free_request(req); 

Puisque je veux créer des structures de requête à différents endroits, une fois avec des littéraux (côté client) et une autre fois avec * chars que j’ai lus depuis un socket (côté serveur), je me demandais s’il existait une fonction permettant de s’assurer que je n’essayez pas de libérer les littéraux tout en me permettant de libérer le message que j’ai créé à l’aide d’un malloc.

Aucune fonction standard ne vous permet de savoir si un pointeur a été alloué dynamicment ou non. Vous devez inclure un indicateur dans votre structure pour vous en informer ou utiliser uniquement des chaînes allouées dynamicment ( strdup est votre ami dans ce cas). En fonction de votre configuration réseau, il peut être plus simple d’utiliser strdup (à vrai dire, il est plus simple d’utiliser strdup ).

Avec strdup :

 struct message* req; req = malloc(sizeof *req); req->message = strdup("TEST"); free_request(req); 

Avec un drapeau:

 struct message { int code; char* message; bool isStatic; // replace to 'char' if bool doesn't exist }; void free_request(struct message* req) { if (!req->isStatic) free(req->message); free(req); } struct message* req; req = malloc(sizeof *req); req->message = "TEST"; req->isStatic = 1; free_request(req); 

De plus, n’oubliez pas de mettre à zéro la mémoire allouée lorsque vous créez un object. Cela pourrait vous épargner beaucoup de peine.

 req = malloc(sizeof *req); memset(req, 0, sizeof *req); 

Cela, et définir req sur NULL partir de free_request n’aura aucun effet. Vous devez soit prendre un struct message** soit le faire vous-même après l’appel de la fonction.

Il n’y a aucun moyen de savoir si vous utilisez un littéral de chaîne (vous pouvez placer des littéraux de chaîne dans une section personnalisée créée par GCC, puis examiner le pointeur de chaîne pour déterminer s’il est contenu dans la section de littéraux). Cependant … il existe un meilleur moyen d’utiliser un modèle de programmation simple.

Allocation avec littéral

Cas normal. Un appel à free (req) fonctionnera comme prévu: libère la structure de la requête.

 struct *req; req = malloc(sizeof(*req)); req->message = "TEST"; 

Allocation avec chaîne dynamic

Dans la suite, some_ssortingng est une chaîne que vous souhaitez stocker en tant que message de demande. Ce peut être soit un littéral, soit un alloué dynamicment. Cela alloue de la mémoire pour la chaîne lorsque la structure elle-même est allouée (et sera libéré automatiquement lorsque la structure est libérée).

 struct *req; req = malloc(sizeof(*req)+strlen(some_ssortingng)+1); req->message = (char *)&req[1]; strcpy(req->message, some_ssortingng); 

Libération

 free(req); 

Edit: Cas général

Notez que le schéma d’allocation ci-dessus pour la dynamic ssortingng est général, il peut être utilisé même si vous ne savez pas si some_ssortingng est une some_ssortingng ou non. Ainsi, une seule fonction qui prend en charge les deux cas, et libérer avec free() vous débarrasse des cas spéciaux.

Je suggérerais d’append un membre à la struct request pour indiquer si request :: message est alloué de manière dynamic et définir ce membre en même temps que vous affectez request::message , puis vérifiez-le avant de libérer la mémoire. C’est un peu brouillon en C.

Notez que ce ne sont pas seulement les littéraux de chaîne qui poseraient le problème, tout pointeur sur des données non allouées dynamicment sur le tas par malloc () ou calloc () échouerait. Détecter simplement “si un caractère pointe vers un littéral de chaîne en C” * Même si cela pouvait être fait de manière portable, cela n’aiderait pas.

Cela est dû au fait que l’emplacement de la mémoire contenant "TEST" est (généralement) en lecture seule et ne se trouve pas sur le tas (généralement parce qu’il se trouve dans une section en lecture seule du programme). Avec seulement un pointeur char* , vous ne pourrez pas savoir s’il pointe vers une chaîne free() ou non. Au lieu de cela, vous devriez allouer un tampon pour req->message et copier les caractères.

 char* str = "TEST"; int len = strlen(str); req->message = malloc(len+1); strcpy(req->message, str); 

Ou, vous pouvez utiliser strdup() comme suggéré par zneak .

Si vous voulez simplement vous assurer que la mémoire au malloc est libérée, vous pouvez appeler

 realloc(req->message,(size_t)0) 

Si l’implémentation de la bibliothèque de mémoire est robuste, cela devrait fonctionner.

Jettes un coup d’oeil à:

 struct request *req; req = calloc(1,sizeof(struct request)); strcpy(req->message = malloc(strlen("TEST")+1),"TEST"); free_request(req); 

C’est ssortingctement conforme à la norme ANSI C. strdup isnt ANSI C.

 req = NULL; 

est redondant.