2013-08-09 8 views
36

Ja testowałem ten kod:Czy jest bezpieczny w użyciu (str1 + str2) .c_str()?

#include <iostream> 
#include <cstdio> 
#include <string> 
using namespace std; 

int main() 
{ 
    string s1("a"),s2("b"); 
    const char * s = (s1+s2).c_str(); 
    printf("%s\n",s); 
} 

powraca "AB".

O ile mi wiadomo, od (s1 +s2) to obiekt tymczasowy i mogą zniknąć w jakiś sposób (nie mam pojęcia o tym), a następnie const char * s może wskazywać na pamięć nieokreślonej i może się po cenach dumpingowych.

Czy korzystanie z .c_str() jest bezpieczne?

+1

Włóż 'ciąg s3 (" O nie! ");' Tuż przed printf, są szanse, że twoje dane wyjściowe ulegną zmianie. (Ale to nie jest gwarantowane.) – Mat

+4

"Możliwy duplikat" jest zasadniczo inny (użyj w obrębie tego samego pełnego wyrażenia). – MSalters

Odpowiedz

57

To nie jest bezpieczne w twoim przykładzie. Jest to bezpieczne jednak w

printf("%s\n", (a + b).c_str()); 

Powodem jest to, że wartości czasowe (jak w wyniku a + b) są zniszczone pod koniec pełnego wyrazu. W twoim przykładzie const char * zachowuje pełne wyrażenie zawierające tymczasowe i dereferencyjne to niezdefiniowane zachowanie.

Najgorsze z „niezdefiniowanej zachowanie” jest to, że rzeczy mogą pozornie działa tak ... (kod UB wywala tylko jeśli robisz swoje demo przed ogromną publicznością, która obejmuje swoim rodzicom ;-))

+9

Czy wykryję tam jakieś bolesne wspomnienia? ;) – catchmeifyoutry

+1

Czy możesz wskazać, gdzie "wartości tymczasowe (takie jak wynik a + b) są niszczone na końcu pełnego wyrażenia." Jest określony w specyfikacji? – SheetJS

+0

@Nirk zobacz odpowiedź Pierre'a Fourgeauda na standardowy tekst. –

29

w tym przykładzie można podać tylko standard:

12.2 przedmiotów przejściowe [class.temporary]

obiekty przejściowe są niszczone jako ostatni krok w ocenie pełnej ekspresji (1,9), który (leksykalnie) zawierają s punkt, w którym zostały stworzone. Dzieje się tak nawet wtedy, gdy ocena kończy się wydaniem wyjątku. Obliczenia wartości i skutki uboczne niszczenia obiektu tymczasowego są powiązane tylko z pełnym wyrażeniem, a nie z żadnym określonym podwyrażeniem.

To po średnikiem swojej linii:

const char * s = (s1+s2).c_str(); // <- Here 

Więc tutaj:

printf("%s\n",s); // This line will now cause undefined behaviour. 

Dlaczego? Ponieważ w miarę niszczenia twojego obiektu, nie wiesz już, co jest teraz w tym miejscu ...

Złe to, że z Niezdefiniowanym zachowaniem Twój program może działać po raz pierwszy, ale ... będzie on upaść na pewno w najgorszym momencie ...

można zrobić:

printf("%s\n", (s1+s2).c_str()); 

To będzie działać, ponieważ obiekt nie jest jeszcze zniszczona (pamiętaj, po średnikiem ...).

+0

Tutaj nie ma żadnego usunięcia; słowo jest "zniszczone". (W C++ mają różne znaczenia.) –

+0

@JamesKanze Masz rację! Właśnie to poprawiłem! Dziękuję za wskazanie tego! –

5

To nie jest bezpieczne, ale można łatwo przypisać do nowej zmiennej, a wskaźnik będzie bezpieczny w zakresie tej zmiennej:

string s1("a"), s2("b") , s3; 
s3 = s1 + s2; 
printf("%s\n", s3.c_str()); 

//other operations with s3 
4

Podobnie jak większość konstrukcji programistycznych, jest „bezpieczny”, jeśli używasz to poprawnie, i nie jest "bezpieczne", jeśli jesteś zaniedbany. W tym przypadku prawidłowe użycie oznacza zwrócenie uwagi na okres istnienia obiektu.Operator + tworzy obiekt tymczasowy, który zostaje zniszczony na końcu instrukcji, a zwrócony const char* nie jest już prawidłowy po instrukcji, która go utworzyła. Tak więc możesz przekazać wynik c_str() bezpośrednio do funkcji, ale nie możesz zapisać wskaźnika i użyć go później.

Powiązane problemy