web-dev-qa-db-fra.com

c ++ new/delete et char *

Est-ce que n'importe qui peut m'aider, pourquoi j'obtiens un message d'erreur en essayant de libérer la mémoire allouée? CTR a détecté que l'application écrivait la mémoire après la fin de la mémoire tampon. 

char *ff (char *s){
    char *s1 = new char [strlen(s)];
    strcpy(s1, s);
    return s1;
}

int _tmain(int argc, _TCHAR* argv[])
{
    char *s = new char [5];

    strcpy(s, "hello");
    char *s2 = ff(s);

    delete []s;     // This works normal
    delete []s2;    // But I get an error on that line
    return 0;
}
17
user1448906
char *s = new char [5];
strcpy(s, "hello");

Provoque comportement indéfini (UB).
Vous écrivez au-delà des limites de la mémoire allouée. Vous avez alloué suffisamment de mémoire pour les caractères 5, mais votre chaîne comporte des caractères 6, y compris le \0

Une fois que votre programme a provoqué cet UB, tous les paris sont ouverts et tout comportement est possible.

Vous avez besoin:

char *s = new char [strlen("hello") + 1];

En fait, la la solution idéale consiste à utiliser std::string et non char *. Ce sont précisément les erreurs que std::string évite. Et il n’est pas vraiment nécessaire d’utiliser char * au lieu de std::string dans votre exemple.
Avec std::string

  • Vous n'avez pas besoin de new quoi que ce soit
  • Vous n'avez pas besoin de delete quoi que ce soit &
  • Vous pouvez tout faire avec std::string, que vous faites avec char *.
44
Alok Save

new char [strlen(s)]; ne compte pas le caractère \0 de fermeture, votre mémoire tampon est donc trop courte d'un caractère.

13
JohnB

strcpy inclut le terminateur nul; strlen ne le fait pas. Écrire:

char *s1 = new char [strlen(s) + 1];
9
ecatmur

De l'homme strcpy (3) :

La fonction strcpy () copie la chaîne pointée par src, y compris l'octet nul final ('\ 0'), vers le tampon pointé vers par dest.

Donc vous devez réserver 6 octets 5 pour la chaîne et 1 pour l'octet NULL

char *s = new char [6];
strcpy(s, "hello");
6
iabdalkader

Jusqu'à présent, toutes les réponses ont porté sur la première ou la deuxième attribution. En résumé, il y a deux modifications à effectuer:

char *s1 = new char [strlen(s) + 1];
...
char *s = new char [5 + 1];

Dans les deux cas, vous devez allouer suffisamment d'espace pour la chaîne plus un octet pour le '\ 0' final.

Comme d'autres l'ont déjà souligné, avec c ++, il est plus facile et plus sûr d'utiliser std::string. Pas de problème avec l'allocation et la libération de mémoire ou en prêtant attention aux octets '\ 0':

std::string ff (const std::string &s){
    std::string s1(s);
    // do something else with s1
    return s1;
}

int main(int argc, char* argv[])
{
    std::string s("hello");
    std::string s2 = ff(s);
    return 0;
}

et s'il ne s'agit que de copier la chaîne:

std::string s("hello");
std::string s2(s);
3
Olaf Dietsche

Vous devez spécifier char *s1 = new char [strlen(s) + 1]; pour libérer de l'espace pour le '\0' qui termine la chaîne.

1
ChrisW

Votre chaîne initiale s ne contient que cinq caractères et ne peut donc pas être terminée par un zéro. "hello" sera copié par strcpy, y compris le terminateur nul, mais vous aurez dépassé le tampon. La strlen a besoin d'être terminée par un null, donc si le null n'y est pas, vous aurez des problèmes. Essayez de changer cette ligne:

char * s = new char [6];

Mieux encore, préférez les fonctions chaîne de style std::string à C - elles sont tout aussi efficaces, beaucoup plus sûres et plus faciles à utiliser. Aussi, essayez d'éviter new et delete à moins que vous ne deviez vraiment les utiliser. Les problèmes que vous rencontrez sont très courants et peuvent facilement être évités.

0
Component 10

Vous avez corrompu le pointeur s2 par 

strcpy(s, "hello");

Parce que s a la taille 5, bien que vous ayez oublié cette stratégie, elle inclut un terminateur de chaîne.

0
Sergei Nikulov