Câu trả lời là: nó phụ thuộc vào tiêu chuẩn C ++ bạn đang biên dịch. Tất cả các mã đều được định dạng hoàn hảo trên tất cả các tiêu chuẩn ‡ ngoại trừ dòng này:
char * s = "My String";
Bây giờ, chuỗi ký tự có kiểu const char[10]
và chúng tôi đang cố gắng khởi tạo một con trỏ không phải const tới nó. Đối với tất cả các kiểu khác ngoài char
họ chuỗi ký tự, việc khởi tạo như vậy luôn là bất hợp pháp. Ví dụ:
const int arr[] = {1};
int *p = arr; // nope!
Tuy nhiên, trước C ++ 11, đối với các ký tự chuỗi, có một ngoại lệ trong §4.2 / 2:
Một chuỗi ký tự (2.13.4) không phải là một chuỗi ký tự rộng có thể được chuyển đổi thành một giá trị của kiểu “ con trỏ đến ký tự ”; [...]. Trong cả hai trường hợp, kết quả là một con trỏ đến phần tử đầu tiên của mảng. Việc chuyển đổi này chỉ được xem xét khi có một loại mục tiêu con trỏ thích hợp rõ ràng, chứ không phải khi có nhu cầu chung là chuyển đổi từ giá trị sang giá trị. [Lưu ý: chuyển đổi này không được dùng nữa . Xem Phụ lục D. ]
Vì vậy, trong C ++ 03, mã hoàn toàn tốt (mặc dù không được dùng nữa) và có hành vi rõ ràng, có thể dự đoán được.
Trong C ++ 11, khối đó không tồn tại - không có ngoại lệ nào như vậy đối với các ký tự chuỗi được chuyển đổi thành char*
, và vì vậy mã cũng không được định hình như int*
ví dụ tôi vừa cung cấp. Trình biên dịch có nghĩa vụ đưa ra chẩn đoán và lý tưởng nhất là trong những trường hợp như vậy là vi phạm rõ ràng đối với hệ thống loại C ++, chúng tôi mong đợi một trình biên dịch tốt không chỉ tuân thủ về mặt này (ví dụ: bằng cách đưa ra cảnh báo) nhưng không thành công ngay.
Lý tưởng nhất là mã không nên biên dịch - nhưng làm trên cả gcc và clang (tôi giả sử vì có thể có rất nhiều mã ngoài đó sẽ bị hỏng với ít lợi ích, mặc dù lỗ hổng hệ thống kiểu này đã không được chấp nhận trong hơn một thập kỷ). Mã không được hình thành và do đó không có ý nghĩa gì để lý luận về hành vi của mã có thể là gì. Nhưng xem xét trường hợp cụ thể này và lịch sử nó đã được cho phép trước đây, tôi không tin rằng việc giải thích mã kết quả như thể nó là một ẩn ý const_cast
, đại loại như:
const int arr[] = {1};
int *p = const_cast<int*>(arr); // OK, technically
Với điều đó, phần còn lại của chương trình hoàn toàn ổn vì bạn thực sự không bao giờ chạm vào s
nữa. Việc đọc một const
đối tượng đã tạo thông qua một const
con trỏ không phải là hoàn toàn OK. Viết một const
đối tượng đã tạo qua một con trỏ như vậy là hành vi không xác định:
std::cout << *p; // fine, prints 1
*p = 5; // will compile, but undefined behavior, which
// certainly qualifies as "unpredictable"
Vì không có sửa đổi s
nào thông qua bất kỳ nơi nào trong mã của bạn, chương trình sử dụng tốt trong C ++ 03, không thể biên dịch trong C ++ 11 nhưng dù sao thì cũng có - và do trình biên dịch cho phép, vẫn không có hành vi không xác định trong đó † . Với sự cho phép mà các trình biên dịch vẫn [không chính xác] diễn giải các quy tắc C ++ 03, tôi không thấy điều gì có thể dẫn đến hành vi "không thể đoán trước". Viết cho s
mặc dù, và tất cả các cược đã tắt. Trong cả C ++ 03 và C ++ 11.
† Tuy nhiên, một lần nữa, theo định nghĩa, mã không hợp lệ không mang lại kỳ vọng về hành vi hợp lý
‡ Ngoại trừ không, hãy xem câu trả lời của Matt McNabb