2012-04-24 26 views
7

Nó có vẻ là một câu hỏi ngớ ngẩn nhưng tôi thực sự cần phải làm rõ điều này:C++ const cast, không chắc chắn nếu điều này là an toàn

Điều này có gây nguy hiểm cho chương trình của tôi không?

Có cần const_cast không?

Nếu tôi thay đổi giá trị con trỏ đầu vào tại chỗ nó sẽ hoạt động an toàn với std::string hoặc nó sẽ tạo ra hành vi không xác định?

Cho đến nay, mối quan tâm duy nhất là điều này có thể ảnh hưởng đến chuỗi "some_text" bất cứ khi nào tôi sửa đổi con trỏ đầu vào và làm cho nó không sử dụng được.

std::string some_text = "Text with some input"; 

char * input = const_cast<char*>(some_text.c_str()); 

Cảm ơn đã cho tôi một số gợi ý, tôi muốn tránh chụp ở chân của riêng tôi

+0

Nói chung, vì 'const_cast' loại bỏ an toàn, bạn nên tránh nó càng nhiều càng tốt. – Mesop

+0

Cảm ơn sự đóng góp của mỗi cơ quan tôi hiểu rằng tôi nên tránh điều này để giữ an toàn –

Trả lời

13

Ví dụ về hành vi xấu: tương tác với việc thực hiện Sao chép ghi trên gcc của gcc.

#include <string> 
#include <iostream> 

int main() { 
    std::string const original = "Hello, World!"; 
    std::string copy = original; 

    char* c = const_cast<char*>(copy.c_str()); 
    c[0] = 'J'; 

    std::cout << original << "\n"; 
} 

Thực hiện tại ideone.

Jello, World!

Sự cố? Như tên của nó, việc thực hiện gcc std::string của gcc sử dụng bộ đệm được chia sẻ được tính lại trong trang bìa. Khi một chuỗi được sửa đổi, việc thực hiện sẽ kiểm tra gọn gàng nếu bộ đệm được chia sẻ vào lúc này, và nếu có, sao chép nó trước khi sửa đổi nó, đảm bảo rằng các chuỗi khác chia sẻ bộ đệm này không bị ảnh hưởng bởi ghi mới (do đó tên, sao chép khi viết).

Bây giờ, với chương trình độc ác của bạn, bạn truy cập bộ đệm dùng chung thông qua phương pháp const (hứa hẹn không sửa đổi bất kỳ điều gì), nhưng bạn sửa đổi nó!

Lưu ý rằng với việc triển khai MSVC, không sử dụng Sao chép khi ghi, hành vi sẽ khác ("Hello, World!" sẽ được in chính xác).

Đây chính là bản chất của Hành vi không xác định.

+16

Bản chất của * Hành vi không xác định *: Thế giới của bạn chuyển thành _jello_. –

+0

Cảm ơn thông tin chi tiết này –

5

Để sửa đổi một const đối tượng vốn bằng cách đúc đi constness của nó sử dụng const_cast là một Không xác định hành vi.

string::c_str() trả về const char *, tức là: con trỏ đến chuỗi kiểu c cố định. Về mặt kỹ thuật, việc sửa đổi điều này sẽ dẫn đến hành vi không xác định.

Lưu ý rằng việc sử dụng const_cast là khi bạn có con trỏ const đến dữ liệu không phải const và bạn muốn sửa đổi dữ liệu không liên tục.

+0

tôi không tìm thấy một tùy chọn chuỗi nào mà thao tác c_str() không có const, có một giải pháp thay thế nào được ưa thích không? –

+0

Có thể trường hợp 'c_str' trả về một con trỏ tới một mảng' char', chứ không phải 'const char'. Vì vậy, chính thức những gì bạn có thể nói là bạn không thể đảm bảo rằng chương trình là chính xác nếu nó sửa đổi các đối tượng thông qua 'đầu vào', không phải là một đảm bảo rằng chương trình là không chính xác. –

+0

@OliverStutz: Bạn phải có một biến khác được phân bổ đầy đủ (mảng hoặc con trỏ) sao chép chuỗi đó và sau đó sửa đổi chuỗi được sao chép sang biến khác. –

1

Có, nó sẽ mang lại nguy hiểm, bởi vì

  1. input điểm để bất cứ điều gì c_str sẽ xảy ra là ngay bây giờ, nhưng nếu some_text bao giờ thay đổi hoặc biến mất, bạn sẽ bị bỏ lại với một con trỏ trỏ đến thùng rác . Giá trị của c_str được đảm bảo chỉ hợp lệ miễn là chuỗi không thay đổi. Và thậm chí, chính thức, chỉ khi bạn không gọi c_str() trên các chuỗi khác nữa.
  2. Tại sao bạn cần bỏ đi const? Bạn không có kế hoạch viết thư cho *input, phải không? Đó là một không-không!
+0

Điều đó thực sự chính xác những gì tôi muốn làm, để sửa đổi chuỗi (ví dụ, loại bỏ các ký tự trùng lặp) vấn đề lớn nhất của tôi là nó thực sự cho phép tôi biên dịch và chạy nó, nhưng chính xác điều đó đã khiến tôi mở bài đăng này. các const đi và sau đó im có thể viết và sửa đổi nó một cách chính xác (cũng có thể nó đã đi về phía nam nhưng nó không nhìn thấy tôi) –

+1

@OliverStutz Những thứ như loại bỏ các ký tự trùng lặp có thể được thực hiện bằng cách gọi được xây dựng trong 'std :: string' chức năng. Nhưng nếu bạn thích các hàm C cũ tốt hơn, hãy đi theo C cũ và tạo ra 'strcpy' trước! –

2

std::string quản lý bộ nhớ riêng trong nội bộ, đó là lý do tại sao nó trả về con trỏ đến bộ nhớ đó trực tiếp với chức năng c_str(). Nó chắc chắn rằng nó liên tục để trình biên dịch của bạn sẽ cảnh báo bạn nếu bạn cố gắng làm modifiy nó.

Sử dụng const_cast theo cách đó theo nghĩa đen sẽ loại bỏ sự an toàn như vậy và chỉ là thực hành được chấp nhận nếu bạn chắc chắn rằng bộ nhớ sẽ không bị sửa đổi.

Nếu bạn không thể đảm bảo điều này thì bạn phải sao chép chuỗi và sử dụng bản sao .; nó chắc chắn là an toàn hơn nhiều để làm điều này trong bất kỳ sự kiện nào (bạn có thể sử dụng strcpy).

2

Xem C++ reference website:

const char* c_str () const; 

"Tạo ra một chuỗi null-chấm dứt của nhân vật (c-string) với nội dung tương tự như các đối tượng chuỗi và trả về nó như một con trỏ đến một mảng các ký tự.

Ký tự null kết thúc được tự động nối.

Mảng được trả về tới vị trí bên trong với không gian lưu trữ cần thiết cho chuỗi ký tự này cộng với ký tự null kết thúc, nhưng các giá trị trong mảng này không được sửa đổi trong chương trình và chỉ đảm bảo không thay đổi cho đến cuộc gọi bên cạnh một hàm thành viên không thường xuyên của đối tượng chuỗi."

4

Đơn giản chỉ cần đúc sẽ không mang lại cho ra một hành vi không xác định. Sửa đổi dữ liệu chỉ vào, tuy nhiên, sẽ. (Also see ISO 14882:98 5.2.7-7).

Nếu bạn muốn con trỏ có thể sửa đổi dữ liệu, bạn có thể có

std::vector<char> wtf(str.begin(), str.end()); 
char* lol= &wtf[0]; 
+7

wtf & lol - foo & bar mới? – gwiazdorrr

+1

Ngoại trừ bây giờ, bạn có một chuỗi không kết thúc null. mà bạn không có với 'char * c = str.c_str(); std :: vector foo (c, c + str.size() + 1); ' – stefaanv

1

Đây là một việc rất tồi tệ. Kiểm tra những gì std :: string :: c_str() does và đồng ý với tôi.

Thứ hai, hãy xem xét lý do tại sao bạn muốn truy cập không phải const vào nội bộ của chuỗi std ::. Rõ ràng bạn muốn sửa đổi các nội dung, bởi vì nếu không bạn sẽ sử dụng một con trỏ const char. Ngoài ra, bạn lo ngại rằng bạn không muốn thay đổi chuỗi gốc. Tại sao không viết

std::string input(some_text); 

Sau đó, bạn có một std :: string mà bạn có thể gây rối với mà không ảnh hưởng bản gốc, và bạn đã std :: chức năng chuỗi thay vì phải làm việc với một C++ nguyên con trỏ ...

+0

Nếu OP cần' char * ', làm như thế này là không tốt, bởi vì bạn có cùng một vấn đề chính xác với chuỗi như với bản gốc! –

1

Một bước ngoặt khác là nó làm cho mã cực kỳ khó duy trì. Trường hợp tại điểm: một vài năm trước, tôi đã phải refactor một số mã có chứa các chức năng dài. Tác giả đã viết chữ ký chức năng để chấp nhận tham số const nhưng sau đó là const_cast ing chúng trong hàm để loại bỏ constness. Điều này đã phá vỡ sự bảo đảm ngụ ý được đưa ra bởi hàm và làm cho nó rất khó để biết liệu tham số đã thay đổi hay không trong phần còn lại của phần thân của mã.

Tóm lại, nếu bạn có quyền kiểm soát chuỗi và bạn cho rằng bạn sẽ cần phải thay đổi chuỗi, hãy không thay đổi nó ngay từ đầu. Nếu không thì bạn sẽ phải lấy một bản sao và làm việc với nó.

1

là UB. Ví dụ, bạn có thể làm một cái gì đó như thế này này:

size_t const size = (sizeof(int) == 4 ? 1024 : 2048); 
int arr[size]; 

mà không cần bất kỳ diễn viên và các comiler sẽ không báo cáo lỗi. Nhưng mã này là bất hợp pháp. Tinh thần là bạn cần cân nhắc hành động mỗi lần.

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