2013-08-09 41 views
36

Tôi đã thử nghiệm mã này:Có an toàn không khi sử dụng (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); 
} 

Nó trả về "ab".

Theo như tôi biết, vì (s1 +s2) là đối tượng tạm thời và có thể biến mất bằng cách nào đó (tôi không biết gì về điều đó), thì const char * s có thể trỏ đến bộ nhớ không xác định và có thể bị bán phá giá.

Vì vậy, việc sử dụng số .c_str() có an toàn không?

+1

Chèn 'string s3 (" Oh no! ");' Ngay trước printf, rất có thể đầu ra của bạn sẽ thay đổi. (Nhưng điều đó không được bảo đảm.) – Mat

+4

"Có thể trùng lặp" về cơ bản là khác nhau (sử dụng trong cùng một biểu thức đầy đủ) – MSalters

Trả lời

57

Không an toàn trong ví dụ của bạn. Đó là an toàn tuy nhiên trong

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

Lý do là giá trị tạm thời (như là kết quả của a + b) bị phá hủy vào cuối của biểu thức đầy đủ. Trong ví dụ của bạn, các const char * tồn tại toàn bộ biểu thức có chứa tạm thời và dereferencing nó là hành vi không xác định.

Phần tồi tệ nhất của "hành vi không xác định" là mọi thứ có thể hoạt động tốt ... (mã UB chỉ bị treo nếu bạn tạo bản trình diễn trước đối tượng rộng lớn bao gồm cha mẹ ;-))

+9

Tôi có phát hiện một số ký ức đau đớn ở đó không? ;) – catchmeifyoutry

+1

Bạn có thể trỏ đến nơi "giá trị tạm thời (như kết quả của dấu + b) bị hủy ở cuối biểu thức đầy đủ." Được chỉ định trong thông số? – SheetJS

+0

@Nirk xem câu trả lời của Pierre Fourgeaud cho văn bản chuẩn. –

29

trong ví dụ mà chúng ta chỉ có thể trích dẫn tiêu chuẩn:

12,2 đối tượng tạm thời [class.temporary]

đối tượng tạm thời bị phá hủy như là bước cuối cùng trong việc đánh giá đầy đủ biểu thức (1.9) mà (từ điển) chứa điểm mà chúng được tạo ra. Điều này đúng ngay cả khi đánh giá đó kết thúc bằng việc ném một ngoại lệ. Các tính toán giá trị và tác dụng phụ của việc phá hủy một đối tượng tạm thời chỉ được liên kết với biểu thức đầy đủ, không phải với bất kỳ biểu thức con cụ thể nào.

Đó là sau dấu chấm phẩy dòng của bạn:

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

Vì vậy, ở đây:

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

Tại sao? Bởi vì khi đối tượng của bạn bị hủy hoại, bạn không biết nữa tại địa điểm này là gì bây giờ ...

Điều xấu ở đây là, với Hành vi không xác định, chương trình của bạn dường như hoạt động lần đầu tiên, nhưng ... Nó sẽ sụp đổ chắc chắn vào thời điểm tồi tệ nhất ...

Bạn có thể làm:

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

Nó sẽ làm việc vì đối tượng không được destructed chưa (hãy nhớ, sau dấu chấm phẩy ...).

+0

Không có xóa ở đây; từ "bị hủy". (Trong C++, chúng có ý nghĩa khác nhau.) –

+0

@JamesKanze Bạn nói đúng! Tôi vừa sửa nó! Cám ơn bạn vì đã chỉ ra điều này ! –

5

Đó không phải là an toàn, nhưng bạn có thể dễ dàng gán cho một biến mới, và con trỏ sẽ được an toàn trong phạm vi của biến:

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

//other operations with s3 
4

Giống như hầu hết các cấu trúc lập trình, đó là "an toàn" nếu bạn sử dụng nó chính xác, và nó không "an toàn" nếu bạn cẩu thả. Trong trường hợp này, sử dụng nó một cách chính xác có nghĩa là chú ý đến thời gian sống của đối tượng.Toán tử + tạo một đối tượng tạm thời bị hủy ở cuối câu lệnh và trả về const char* không còn hợp lệ sau khi câu lệnh tạo ra nó nữa. Vì vậy, bạn có thể chuyển kết quả của c_str() trực tiếp đến một hàm, nhưng bạn không thể lưu con trỏ và sử dụng nó sau này.

Các vấn đề liên quan