web-dev-qa-db-fra.com

Comment retourner un std :: string.c_str ()

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.

29
user3210526

Ce qui se passe dans ce code est:

const char * returnCharPtr()
{
    std::string someString("something");
    return someString.c_str();
}
  1. exemple de std::string est créé - c'est un objet avec une durée de stockage automatique
  2. le pointeur vers la mémoire interne de cette chaîne est renvoyé
  3. l'objet someString est détruit et sa mémoire interne est nettoyée
  4. l'appelant de cette fonction reçoit pointeur pendant (pointeur invalide) qui donne comportement indéfini

La meilleure solution: retourne un objet :

std::string returnString()
{
    std::string someString("something");
    return someString;
}
20
LihO

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());
}
12
Mr.C64

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.

6
alk

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!.
}
3
ArthurChamz

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());
1
milleniumbug

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.

1
coyotte508

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.

1
user2033018

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++;
}
1
MrDuk

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.

1
xen-0

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;
}
1
R Sahu