2012-04-22 52 views
13

Tôi biết rằng ví dụ: "hello" thuộc loại const char*. Vì vậy, câu hỏi của tôi là:Tại sao có thể gán một ký tự const * thành char *?

  1. Làm sao chúng ta có thể gán một chuỗi chữ như "hello" đến một phi const char* như thế này:

    char* s = "hello"; // "hello" is type of const char* and s is char* 
            // and we know that conversion from const char* to 
            // char* is invalid 
    
  2. Là một chuỗi chữ như "hello", mà sẽ mất bộ nhớ trong tất cả chương trình của tôi, hoặc nó giống như biến tạm thời sẽ bị phá hủy khi tuyên bố kết thúc?

+2

Điều này chỉ được phép cho khả năng tương thích với C. Nói chung trong C++ nó được coi là không được chấp nhận và trình biên dịch tốt cho cảnh báo. – iammilind

Trả lời

22

Thực tế, "hello" là loại char const[6].

Nhưng ý chính của câu hỏi vẫn đúng - tại sao C++ cho phép chúng tôi chỉ định vị trí bộ nhớ chỉ đọc cho loại không phải là const?

Lý do duy nhất cho điều này là khả năng tương thích ngược với mã C cũ, không biết const. Nếu C++ đã được nghiêm ngặt ở đây nó sẽ bị phá vỡ rất nhiều mã hiện có.

Điều đó nói rằng, hầu hết các trình biên dịch có thể được định cấu hình thành cảnh báo về mã như bị phản đối hoặc thậm chí làm như vậy theo mặc định. Hơn nữa, C++ 11 không cho phép điều này hoàn toàn nhưng các trình biên dịch có thể không thực thi nó.


cho người hâm mộ Standerdese:
[Ref 1]C++ 03 Tiêu chuẩn: §4.2/2

Một chuỗi chữ (2.13.4) mà không phải là một rộng chuỗi chữ có thể được chuyển đổi thành một rvalue thuộc loại “pointer to char”; một chuỗi ký tự lớn có thể được chuyển đổi thành một giá trị rvalue kiểu "pointer to wchar_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. Chuyển đổi này chỉ được xem xét khi có loại mục tiêu con trỏ thích hợp rõ ràng và không phải khi có nhu cầu chung để chuyển đổi từ một giá trị thành giá trị. [Lưu ý: chuyển đổi này không được dùng nữa. Xem Phụ lục D.] Với mục đích xếp hạng ở độ phân giải quá tải (13.3.3.1.1), chuyển đổi này được coi là chuyển đổi mảng thành con trỏ, sau đó là chuyển đổi tiêu chuẩn (4.4). [Ví dụ: "abc" được chuyển thành "con trỏ thành const char" dưới dạng chuyển đổi mảng thành con trỏ và sau đó là "con trỏ thành char" làm chuyển đổi đủ điều kiện. ]

C++ 11 chỉ đơn giản loại bỏ báo giá ở trên ngụ ý rằng đó là mã bất hợp pháp trong C++ 11.

[Ref 2]C99 tiêu chuẩn 6.4.5/5 "Chuỗi Literals - Semantics":

Trong giai đoạn dịch 7, một byte hoặc mã có giá trị không được gắn cho mỗi ký tự multibyte chuỗi kết quả từ chuỗi ký tự hoặc chữ. Chuỗi ký tự multibyte sau đó được sử dụng để khởi tạo một mảng thời gian lưu trữ tĩnh và độ dài vừa đủ để chứa chuỗi.Đối với các chuỗi ký tự chuỗi ký tự, các phần tử mảng có kiểu char và được khởi tạo với các byte riêng lẻ của chuỗi ký tự nhiều byte; đối với các chuỗi ký tự chuỗi rộng, các phần tử mảng có kiểu wchar_t và được khởi tạo với chuỗi ký tự rộng ...

Không xác định xem các mảng này có khác biệt hay không. Nếu chương trình tìm cách sửa đổi một mảng như vậy, hành vi không xác định.

+1

Đã thêm các trích dẫn có liên quan từ tiêu chuẩn.Hy vọng bạn sẽ không quan tâm đến điều đó. –

+0

MSVC trong chế độ gỡ lỗi sẽ ngăn bạn viết bằng một chuỗi ký tự theo thời gian chạy, GCC tại thời gian biên dịch (nếu có thể). Tuy nhiên, có một tùy chọn, tôi tin rằng đó là chuỗi -wwrite hoặc như vậy sẽ bật chế độ tương thích và đặt chuỗi ký tự trong bộ nhớ R/W –

+2

@Als Bạn đã thực hiện một điều đơn giản và làm cho nó xấu xí. ;-) Nhưng cảm ơn.Tôi thực sự muốn thêm điều đó là tốt nhưng tôi không thể tìm thấy các đoạn có liên quan trong dự thảo của tôi về C++ 11 ở tất cả. 2.14.3 xác định loại và phụ lục chỉ định tính không hợp lệ của chuyển đổi, nhưng không có gì cho thấy rằng điều này đã bị phản đối nhưng hợp lệ trong C++ 03. –

1

Chỉ cần sử dụng một string:

std::string s("hello"); 

Đó sẽ là C++ cách. Nếu bạn thực sự phải sử dụng char, bạn sẽ cần phải tạo một mảng và sao chép nội dung trên đó.

2

là chuỗi chữ như "hello" sẽ mất bộ nhớ trong tất cả chương trình của tôi, tất cả chỉ giống như một biến tạm thời sẽ bị hủy khi tuyên bố kết thúc.

Dữ liệu này được lưu giữ trong dữ liệu chương trình, do đó, dữ liệu này sẽ có sẵn trong suốt thời gian của chương trình. Bạn có thể trả lại con trỏ và tham chiếu đến dữ liệu này từ phạm vi hiện tại.

Lý do duy nhất tại sao const char* đang được truyền tới char* là sự tương thích với c, như gọi hệ thống winapi. Và dàn diễn viên này được thực hiện không rõ ràng không giống như bất kỳ đúc const khác.

+0

vì vậy const char * là loại duy nhất có thể được đúc thành char * không rõ ràng bởi trình biên dịch.? – AlexDan

+0

AlexDan, 'const char *' là kiểu duy nhất có thể được truyền sang dạng không phải const của nó (ví dụ, 'const int *' không thể được đúc thành 'int *' một cách không rõ ràng). Nhưng bạn có thể điều chỉnh trình biên dịch để tạo ra một cảnh báo. –

+2

cũng, * cast * là một động từ bất quy tắc, 3 dạng: * cast-cast-cast * –

-3

Trong ví dụ của bạn, bạn không chỉ định, nhưng đang xây dựng. std :: string, ví dụ, có một constructor std::string(const char *) (thực sự nó phức tạp hơn, nhưng nó không quan trọng). Và, tương tự, char * (nếu nó là một loại thay vì một con trỏ đến một loại) có thể có một const char * constructor, đó là sao chép bộ nhớ. Tôi thực sự không biết trình biên dịch thực sự hoạt động như thế nào, nhưng tôi nghĩ nó có thể tương tự như những gì tôi đã mô tả ở trên:

+2

Điều này đơn giản là không chính xác. Không có bản sao được thực hiện, và sửa đổi chuỗi thông qua con trỏ là UB. –

+0

Không có bản sao của "Hello" trên ngăn xếp! Nó là một chuỗi chữ mà sẽ được tạo ra trong một số [phân đoạn dữ liệu] toàn cầu (https://secure.wikimedia.org/wikipedia/en/wiki/Data_segment) (nhưng đây là một chi tiết cụ thể về việc triển khai thực hiện). – Praetorian

+0

@KonradRudolph, tôi chưa bao giờ nói nó đúng. Tôi buồn, rằng nó có thể là như thế này. Cảm ơn các liên kết anyway. – Steed

1

Câu trả lời cho câu hỏi thứ hai của bạn là biến số s được lưu trữ trong RAM dưới dạng kiểu con trỏ tới ký tự. Nếu nó là toàn cầu hoặc tĩnh, nó được phân bổ trên heap và vẫn còn đó cho cuộc đời của chương trình đang chạy. Nếu đó là biến cục bộ ("tự động"), nó được cấp phát trên ngăn xếp và vẫn ở đó cho đến khi hàm hiện tại trả về. Trong cả hai trường hợp, nó chiếm số lượng bộ nhớ cần thiết để giữ một con trỏ.

Chuỗi "Hello" là một hằng số và được lưu trữ như một phần của chương trình, cùng với tất cả các hằng số và trình khởi tạo khác. Nếu bạn xây dựng chương trình của mình để chạy trên một thiết bị, chuỗi sẽ được lưu trữ trong ROM.

Lưu ý rằng, vì chuỗi là hằng số và s là một con trỏ, không cần sao chép. Con trỏ s chỉ đơn giản trỏ đến bất cứ nơi nào chuỗi được lưu trữ.

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