2016-03-14 18 views
7

Tôi đang sử dụng thư viện C trong C++ và đã viết một trình bao bọc. Tại một thời điểm tôi cần phải chuyển đổi một std::string thành một chuỗi kiểu c. Có một lớp với một hàm, trả về một chuỗi. Việc đưa chuỗi trả về hoạt động nếu chuỗi ngắn, nếu không thì sẽ không. Dưới đây là một ví dụ đơn giản và giảm minh họa vấn đề này:Đúc c_str() chỉ hoạt động đối với các chuỗi ngắn

#include <iostream> 
#include <string> 

class StringBox { 
public: 
    std::string getString() const { return text_; } 

    StringBox(std::string text) : text_(text){}; 

private: 
    std::string text_; 
}; 

int main(int argc, char **argv) { 
    const unsigned char *castString = NULL; 
    std::string someString = "I am a loooooooooooooooooong string"; // Won't work 
    // std::string someString = "hello"; // This one works 

    StringBox box(someString); 

    castString = (const unsigned char *)box.getString().c_str(); 
    std::cout << "castString: " << castString << std::endl; 

    return 0; 
} 

Thực thi các tập tin trên bản in này ra cửa sổ Console:

castString:

trong khi nếu tôi trao đổi các ý kiến ​​về someString , nó in chính xác

castString: hello

Làm cách nào có thể?

Trả lời

16

Bạn đang gọi c_str trên đối tượng chuỗi tạm thời được retuned bởi hàm thành viên getString(). Con trỏ được trả về bởi c_str() chỉ hợp lệ miễn là đối tượng chuỗi ban đầu tồn tại, do đó, ở cuối dòng mà bạn gán castString nó kết thúc là một con trỏ lơ lửng. Chính thức, điều này dẫn đến hành vi không xác định.

Vậy tại sao tính năng này hoạt động đối với các chuỗi ngắn? Tôi nghi ngờ rằng bạn đang thấy các hiệu ứng của Tối ưu hóa Chuỗi Ngắn, một tối ưu hóa cho các chuỗi nhỏ hơn một độ dài nhất định dữ liệu ký tự được lưu trữ bên trong các byte của đối tượng chuỗi thay vì trong heap. Có thể là chuỗi tạm thời được trả lại được lưu trữ trên ngăn xếp, vì vậy khi nó được dọn dẹp, không có sự thỏa thuận nào xảy ra và con trỏ đến đối tượng chuỗi đã hết hạn vẫn giữ các chuỗi chuỗi cũ của bạn. Điều này có vẻ phù hợp với những gì bạn đang nhìn thấy, nhưng nó vẫn không có nghĩa là những gì bạn đang làm là một ý tưởng tốt. :-)

+0

Cảm ơn, điều đó có ý nghĩa! Điều thú vị là mã hoạt động hoàn toàn tốt đẹp trong nhiều tháng trên hệ thống khác của tôi. Chỉ bây giờ tôi đã cập nhật Ubuntu từ 14.04 đến 15.10 và cài đặt lại mọi thứ đã thay đổi. Tôi đoán đó là hành vi không xác định ... – Cat

+0

Và cảm ơn bạn đã xây dựng. Bạn có thể giải thích ý của bạn bằng cách "không phải là một ý tưởng hay" không? Hoặc là bạn đề cập đến sai lầm của tôi gọi c_str() trên một đối tượng trả về bởi một chức năng? – Cat

+0

Tôi đoán sửa chữa dễ dàng là: 'std :: cout <<" castString: "<< (const unsigned char *) box.getString(). C_str() << std :: endl;' – chqrlie

6

box.getString()tạm thời ẩn danh. c_str() chỉ hợp lệ cho độ dài của biến.

Vì vậy, trong trường hợp của bạn, c_str()bị vô hiệu khi bạn truy cập vào std::cout. Hành vi đọc nội dung con trỏ là không xác định.

(Điều thú vị là hành vi của chuỗi ngắn của bạn là thể khác nhau do std::string lưu trữ các chuỗi ngắn theo một cách khác.)

+1

Tôi sẽ s/length/life – NathanOliver

+0

Cảm ơn sự giúp đỡ của bạn, tôi hiểu ngay bây giờ! – Cat

5

Như bạn quay trở lại theo giá trị

box.getString() là tạm thời và do đó

box.getString().c_str() chỉ hợp lệ trong biểu thức, sau đó nó là một con trỏ lơ lửng.

Bạn có thể khắc phục điều đó với

const std::string& getString() const { return text_; } 
+0

Cảm ơn sự giúp đỡ của bạn! – Cat

+0

Chỉ cần rõ ràng: điều này khắc phục vấn đề vì vì giá trị trả về 'getString()' bây giờ là tham chiếu đến '_text' được giữ bởi' StringBox' (và không phải là một tạm thời ẩn danh như trong mã của bạn), kết quả của ' .c_str() 'là hợp lệ cho tuổi thọ của' _text', hoặc bất kỳ thao tác sửa đổi nào khác trên '_text'. –

5

box.getString() tạo ra một tạm thời. Gọi số c_str() trên đó cung cấp cho bạn một con trỏ đến một tạm thời. Sau khi tạm thời chấm dứt tồn tại, tức là con trỏ không hợp lệ, một con trỏ lơ lửng .

Sử dụng con trỏ lơ lửng là Hành vi chưa xác định.

4

Trước hết, mã của bạn có UB độc lập với chiều dài của chuỗi: Vào cuối

castString = (const unsigned char *)box.getString().c_str(); 

chuỗi trả về bởi getString bị phá hủy và castString là một con trỏ tòn ten vào bộ đệm bên trong của đối tượng chuỗi bị hủy.

Lý do mã của bạn "hoạt động" cho chuỗi nhỏ có thể là Chuỗi nhỏ Tối ưu hóa: Chuỗi ngắn thường được lưu trong chính chuỗi thay vì được lưu trong mảng được phân bổ động và dường như bộ nhớ đó vẫn còn chưa sửa đổi trong trường hợp của bạn.

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