2012-11-06 9 views
17

Czy ktoś może mi pomóc, dlaczego otrzymuję komunikat o błędzie podczas próby zwolnienia przydzielonej pamięci: wykryto uszkodzenie sterty. Wykryto CTR, aplikacja napisała pamięć po końcu bufora sterty.C++ nowy/usuń i zwiąż *

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; 
} 
+9

Oczywiście, jest to tylko ćwiczenia i prawdziwy ustawienie zamiast tego używasz 'std :: string', prawda? –

+0

@ MatthieuM. Całkowita racja. Nie wolno mi używać std :: string; – user1448906

Odpowiedz

41
char *s = new char [5]; 
strcpy(s, "hello"); 

Powoduje zachowanie niezdefiniowane (UB).
Piszecie poza granicami przydzielonej memery. Przydzieliłeś wystarczającą ilość pamięci dla znaków 5, ale Twój ciąg znaków zawiera 6 znaków, w tym \0.

Po tym, jak program wygenerował ten UB, wszystkie zakłady są wyłączone i możliwe jest zachowanie.

potrzebował:

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

W rzeczywistości idealnym rozwiązaniem jest użycie std::string i nie char *. Są to błędy, które omijają. W twoim przykładzie nie ma potrzeby używania char * zamiast std::string.
Z std::string:

  • Nie trzeba do niczego new
  • Nie trzeba delete niczego &
  • można zrobić wszystko z std::string, że robisz z char *.
+1

Uzgodniono, że używanie 'std :: string' jest najlepszym rozwiązaniem. Innym sposobem pisania ulepszonej alokacji 'new' podczas twojej odpowiedzi będzie" char * s = new char [sizeof ("hello")] ', unikając wywołania' strlen() 'w czasie wykonywania (chociaż inteligentny kompilator może to zoptymalizować). To oczywiście działa tylko wtedy, gdy masz stały ciąg znaków, co prawdopodobnie nie jest typowym przypadkiem. –

13

new char [strlen(s)]; nie liczy się charakter zamykanie \0, więc bufor jest zbyt krótkie, by jeden znak.

9

strcpy obejmuje terminator zerowy; strlen nie. Napisz:

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

Twój początkowy łańcuch s ma tylko pięć znaków, więc nie może być zakończony znakiem null. "hello" zostanie skopiowany przez strcpy, w tym terminator o wartości NULL, ale bufor zostanie przekroczony. strlen wymaga, aby była zakończona zerem, więc jeśli nie ma pustki, będziesz mieć problemy. Spróbuj zmienić tę linię:

char * s = new char [6];

Jeszcze lepiej, wolisz std::string od funkcji ciągów w stylu C - są one równie wydajne i dużo bezpieczniejsze i łatwiejsze w użyciu. Staraj się także unikać new i delete, chyba że naprawdę musisz ich użyć. Problemy, które otrzymujesz, są bardzo powszechne i można ich łatwo uniknąć.

1

Musisz podać char *s1 = new char [strlen(s) + 1];, aby zrobić miejsce dla '\0', która kończy łańcuch.

6

Od mężczyzny strcpy(3):

strcpy() kopiuje łańcuch znaków wskazywany przez src, tym kończącego NUL ('\ 0'), do bufora wskazywanego przez dest .

Więc trzeba zarezerwować 6 bajtów 5 dla struny i 1 dla NULL bajt

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

Masz uszkodzony wskaźnik s2 przez

strcpy(s, "hello"); 

Ponieważ s ma rozmiar 5, podczas gdy przegapiłeś, że strcpy zawiera terminator ciągu.

3

Wszystkie dotychczasowe odpowiedzi dotyczyły pierwszej lub drugiej alokacji. Podsumowując, istnieje dwa zmiany należy wprowadzić:

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

W obu przypadkach należy przeznaczyć wystarczającą ilość miejsca dla struny plus jeden bajt na kończące „\ 0”.

Jak już inni zauważyli, w języku C++ łatwiejsze i bezpieczniejsze jest używanie std::string. Bez kłopotów z alokacji i zwalniania pamięci lub zwracając uwagę na „\ 0” bajtów:

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; 
} 

i jeśli to tylko skopiowanie ciągu:

std::string s("hello"); 
std::string s2(s);