J'ai une méthode qui retourne le pointeur char constant. Il utilise un std::string
Et retourne finalement son pointeur c_str()
char.
const char * returnCharPtr()
{
std::string someString;
// some processing!.
return someString.c_str();
}
J'ai reçu un rapport de l'outil COVERITY que ce qui précède n'est pas un bon usage. J'ai googlé et j'ai découvert que le pointeur char retourné, serait invalidé dès que someString
rencontrera sa destruction.
Compte tenu de cela, comment peut-on résoudre ce problème? Comment renvoyer le pointeur char avec précision?
Le retour de std::string
Résoudrait ce problème. Mais je veux savoir s'il existe d'autres moyens de le faire.
Ce qui se passe dans ce code est:
const char * returnCharPtr()
{
std::string someString("something");
return someString.c_str();
}
std::string
est créé - c'est un objet avec une durée de stockage automatiquesomeString
est détruit et sa mémoire interne est nettoyéeLa meilleure solution: retourne un objet :
std::string returnString()
{
std::string someString("something");
return someString;
}
En C++, la chose la plus simple à faire est de simplement retourner un std::string
(Qui est également efficace grâce à des optimisations comme RVO et la sémantique de déplacement C++ 11):
std::string returnSomeString()
{
std::string someString;
// some processing...
return someString;
}
Si vous avez vraiment besoin d'un pointeur C char*
Brut, vous pouvez toujours appeler .c_str()
sur la valeur retournée, par ex.
// void SomeLegacyFunction(const char * psz)
// .c_str() called on the returned string, to get the 'const char*'
SomeLegacyFunction( returnSomeString().c_str() );
Si vous voulez vraiment retourner un pointeur char*
À partir de la fonction, vous pouvez dynamiquement allouer de la mémoire de chaîne sur le tas (par exemple en utilisant new[]
), Et renvoyer un pointeur à cette:
// NOTE: The caller owns the returned pointer,
// and must free the string using delete[] !!!
const char* returnSomeString()
{
std::string someString;
// some processing...
// Dynamically allocate memory for the returned string
char* ptr = new char[someString.size() + 1]; // +1 for terminating NUL
// Copy source string in dynamically allocated string buffer
strcpy(ptr, someString.c_str());
// Return the pointer to the dynamically allocated buffer
return ptr;
}
Une alternative est de fournir un pointeur de tampon de destination et la taille du tampon (pour éviter les dépassements de tampon!) Comme paramètres de fonction:
void returnSomeString(char* destination, size_t destinationSize)
{
std::string someString;
// some processing...
// Copy string to destination buffer.
// Use some safe string copy function to avoid buffer overruns.
strcpy_s(destination, destinationSize, someString.c_str());
}
Comme cette question est signalée C, procédez comme suit:
#define _POSIX_C_SOURCE 200809L
#include <string.h>
const char * returnCharPtr()
{
std::string someString;
// some processing!.
return strdup(someString.c_str()); /* Dynamically create a copy on the heap. */
}
N'oubliez pas de free()
ce que la fonction a renvoyé si elle n'est plus utile.
Eh bien, la COUVERTURE est correcte. La raison de l'échec de votre approche actuelle est que l'instance de std::string
vous avez créé à l'intérieur de la fonction ne sera valide que tant que cette fonction est en cours d'exécution. Une fois que votre programme quitte la portée de la fonction, le destructeur de std :: string sera appelé et ce sera la fin de votre chaîne.
Mais si ce que vous voulez, c'est une chaîne en C, que diriez-vous ...
const char * returnCharPtr()
{
std::string someString;
// some processing!.
char * new_string = new char[someString.length() + 1];
std::strcpy(new:string, someString.c_str());
return new_string;
}
Mais attendez ... c'est presque exactement comme retourner un std::string
, n'est-ce pas?
std::string returnCharPtr()
{
std::string someString;
// some processing!.
return new_string;
}
Cela copiera votre chaîne dans une nouvelle en dehors de la portée de la fonction. Cela fonctionne, mais il crée une nouvelle copie de la chaîne.
Grâce à Return Value Optimization, cela ne créera pas de copie (merci pour toutes les corrections!).
Ainsi, une autre option consiste à passer le paramètre en argument, donc vous traitez votre chaîne dans une fonction mais ne créez pas de nouvelle copie. :
void returnCharPtr(std::string & someString)
{
// some processing!.
}
Ou, encore une fois, si vous voulez des cordes en C, vous devez faire attention à la longueur de votre chaîne:
void returnCharPtr(char*& someString, int n) // a reference to pointer, params by ref
{
// some processing!.
}
Le problème est que someString
est détruit à la fin de la fonction, et la fonction renvoie le pointeur vers des données inexistantes.
Ne retournez pas .c_str()
de chaîne qui pourrait être détruite avant d'utiliser le pointeur char retourné.
Au lieu de...
const char* function()
{
std::string someString;
// some processing!
return someString.c_str();
}
//...
useCharPtr(function());
utilisation
std::string function()
{
std::string someString;
// some processing!
return someString;
}
//...
useCharPtr(function().c_str());
Une solution qui n'a pas été évoquée dans les autres réponses.
Dans le cas où votre méthode est membre d'une classe, comme ceci:
class A {
public:
const char *method();
};
Et si l'instance de classe va vivre au-delà de l'utilité du pointeur, vous pouvez faire:
class A {
public:
const char *method() {
string ret = "abc";
cache.Push_back(std::move(ret));
return cache.last().c_str();
}
private:
vector<string> cache; //std::deque would be more appropriate but is less known
}
De cette façon, les pointeurs seront valables jusqu'à la destruction de A
.
Si la fonction ne fait pas partie d'une classe, elle peut toujours utiliser une classe pour stocker les données (comme une variable static
de la fonction ou une instance de classe externe qui peut être référencée globalement, ou même un static
membre d'une classe). Des mécanismes peuvent être mis en place pour supprimer les données après un certain temps, afin de ne pas les conserver indéfiniment.
La meilleure façon serait de renvoyer un std::string
, qui gère automatiquement la mémoire. Si, d'un autre côté, vous vouliez vraiment retourner un const char*
qui pointe vers une certaine mémoire allouée par vous depuis returnCharPtr
, alors elle devrait être libérée par quelqu'un d'autre explicitement.
Rester avec std::string
.
Vous pouvez passer un pointeur sur votre chaîne et demander à la méthode de la manipuler directement (c'est-à-dire en évitant complètement les retours)
void returnCharPtr(char* someString)
{
// some processing!
if(someString[0] == 'A')
someString++;
}
Vos options sont:
Retour std::string
Passez un tampon à returnCharPtr()
qui contiendra le nouveau tampon de caractères. Cela vous oblige à vérifier que le tampon fourni est suffisamment grand pour contenir la chaîne.
Créez un nouveau tableau char
dans returnCharPtr()
, copiez le tampon dans le nouveau et retournez un pointeur sur celui-ci. Cela nécessite que l'appelant appelle explicitement delete []
Sur quelque chose qu'ils n'ont pas explicitement créé avec new
, ou le place immédiatement dans une classe de pointeur intelligent. Cette solution serait améliorée si vous renvoyiez un pointeur intelligent, mais il est vraiment plus logique de renvoyer un std::string
Directement.
Choisissez le premier; retourner std::string
. C'est de loin l'option la plus simple et la plus sûre.
Si vous avez la liberté de modifier la valeur de retour de returnCharPtr
, remplacez-la par std::string
. Ce sera la méthode la plus propre pour renvoyer une chaîne. Si vous ne le pouvez pas, vous devez allouer de la mémoire à la chaîne renvoyée, copiez-la depuis std::string
et renvoie un pointeur sur la mémoire allouée. Vous devez également vous assurer que vous supprimez la mémoire dans la fonction d'appel. Étant donné que l'appelant sera responsable de la libération de la mémoire, je changerais la valeur de retour en char*
.
char* returnCharPtr()
{
std::string someString;
// some processing!.
char* cp = new char[someString.length()+1];
strcpy(cp, someString.c_str());
return cp;
}