2012-01-12 28 views
26

Mã của tôi chuyển đổi chuỗi C++ thành CStrings thường xuyên, và tôi tự hỏi nếu chuỗi gốc được cấp phát trên ngăn xếp, liệu CString có được cấp phát trên ngăn xếp không? Ví dụ:string.c_str() deallocation cần thiết?

string s = "Hello world"; 
char* s2 = s.c_str(); 

Sẽ phân bổ s2 trên ngăn xếp hoặc trong heap? Nói cách khác, tôi có cần xóa s2 không?

Ngược lại, nếu tôi có mã này:

string s = new string("Hello, mr. heap..."); 
char* s2 = s.c_str(); 

sẽ s2 tại được trên heap, như nguồn gốc của nó là trên heap?

Để làm rõ, khi tôi hỏi xem s2 có nằm trên heap không, tôi biết con trỏ nằm trên ngăn xếp. Tôi hỏi nếu những gì nó trỏ đến sẽ có trên đống hoặc ngăn xếp.

Trả lời

32
string s = "Hello world"; 
char* s2 = s.c_str(); 

S2 có được phân bổ trên ngăn xếp hoặc trong heap không? Nói cách khác ... Tôi có cần xóa s2 không?

s2 có trên ngăn xếp, có. Tuy nhiên, nó là một con trỏ đến một ký tự (trong trường hợp này xảy ra là ký tự đầu tiên trong một biểu diễn ASCIIZ của nội dung văn bản của s). Bản thân văn bản đó là nơi đối tượng s cảm thấy giống như việc xây dựng biểu diễn đó.Việc triển khai được phép thực hiện điều đó theo cách mà họ thích, nhưng lựa chọn thực hiện quan trọng đối với std::string là liệu chúng có cung cấp "tối ưu hóa chuỗi ngắn" cho phép các chuỗi rất ngắn được nhúng trực tiếp trong đối tượng s và liệu "Hello world" có đủ ngắn hay không được hưởng lợi từ tối ưu hóa mà:

  • nếu như vậy, sau đó s2 sẽ trỏ đến ngăn xếp bộ nhớ được phân bổ trong s
  • khác, bên s sẽ có một con trỏ đến/đống phân bổ bộ nhớ lưu trữ giải phóng trong đó "Hello world \ 0 "nội dung có địa chỉ được trả về bởi .c_str() sẽ xuất hiện và s2 sẽ là bản sao của giá trị đó.

Lưu ý rằng c_str()const, vì vậy đối với mã của bạn để biên dịch bạn cần phải thay đổi để const char* s2 = ....

Bạn không cần xóa s2, không. Dữ liệu mà các điểm s2 vẫn thuộc sở hữu và được quản lý bởi đối tượng s, sẽ bị vô hiệu hóa bởi bất kỳ lệnh gọi nào theo phương thức không const của s hoặc bằng s không nằm ngoài phạm vi.

string s = new string("Hello, mr. heap..."); 
char* s2 = s.c_str(); 

sẽ S2 tại được trên heap, như nguồn gốc của nó là trên heap?

Mã này không biên dịch, vì s không phải là con trỏ và chuỗi không có hàm tạo như string(std::string*). Bạn có thể thay đổi nó hoặc là:

string* s = new string("Hello, mr. heap..."); 

... hoặc ...

string s = *new string("Hello, mr. heap..."); 

Sau đó tạo ra một rò rỉ bộ nhớ và phục vụ không có mục đích hữu ích, vì vậy chúng ta hãy giả định trước đây. Sau đó:

char* s2 = s.c_str(); 

... cần phải trở thành ...

char* s2 = s->c_str(); 

sẽ S2 tại được trên heap, như nguồn gốc của nó là trên heap?

Có. Trong tất cả các tình huống, đặc biệt là nếu s chính nó là trên heap, sau đó:

  • thậm chí nếu có một chuỗi ngắn tối ưu hóa bộ đệm bên trong sc_str() mang lại một con trỏ, nó phải nằm trên đống, nếu không
  • nếu s sử dụng con trỏ để nhớ thêm để lưu trữ văn bản, bộ nhớ đó cũng sẽ được cấp phát từ heap.

Nhưng một lần nữa, ngay cả khi biết chắc chắn rằng s2 điểm để bộ nhớ heap phân bổ, mã của bạn không cần phải deallocate bộ nhớ đó - nó sẽ được thực hiện tự động khi s bị xóa:

string* s = new string("Hello, mr. heap..."); 
const char* s2 = s->c_str(); 
...use s2 for something... 
delete s; // "destruct" s and deallocate the heap used for it... 

Trong số tất nhiên, tốt hơn hết là chỉ sử dụng string s("xyz"); trừ khi bạn cần một tuổi thọ vượt quá phạm vi địa phương và std::unique_ptr<std::string> hoặc std::shared_ptr<std::string> nếu không.

12

c_str() trả về con trỏ tới bộ đệm trong trong đối tượng string - bạn không bao giờ free()/delete nó.

Nó chỉ hợp lệ miễn là string nó trỏ vào nằm trong phạm vi. Ngoài ra, nếu bạn gọi phương thức không phải là const của đối tượng string thì nó không còn được đảm bảo là hợp lệ nữa.

http://www.cplusplus.com/reference/string/string/c_str/

(Chỉnh sửa cho rõ ràng dựa trên ý kiến ​​dưới đây)

+0

Thú vị, vì vậy, bạn sẽ không bao giờ giải phóng char * vì làm như vậy sẽ giải phóng dữ liệu nội bộ trong chuỗi? c_str() theo nghĩa đen giữ cùng địa chỉ với dữ liệu thực tế mà chuỗi đang sử dụng? Nhiều cuộc gọi đến .c_str() sẽ trả lại cùng một địa chỉ theo cách đó? (Chỉ cần làm rõ vì vậy tôi biết rằng tôi hiểu) –

+0

@Georges câu trả lời cho một số câu hỏi đó phụ thuộc vào việc bạn đang sử dụng tiêu chuẩn mới nhất hay tiêu chuẩn cũ. Bạn có thể muốn làm rõ một trong những bạn đang quan tâm. –

+0

@ R.MartinhoFernandes Tốt hơn đi với các tiêu chuẩn mới nhất. Sẽ là thú vị mặc dù để biết làm thế nào gần đây tiêu chuẩn này là –

0

Điều đó phụ thuộc. Nếu tôi nhớ chính xác, CString tạo một bản sao của chuỗi đầu vào, vì vậy không, bạn sẽ không cần phải có bất kỳ thói quen phân bổ heap đặc biệt nào.

4

std::string::c_str() trả về một const char*, không phải là char *. Đó là một dấu hiệu khá tốt mà bạn không cần phải giải phóng nó. Bộ nhớ được quản lý bởi cá thể (xem một số chi tiết trong ví dụ this link), vì vậy nó chỉ hợp lệ trong khi cá thể chuỗi hợp lệ.

+6

Độ chụm của đối tượng được trỏ đến không phải là dấu hiệu cho thấy nó không không cần phải được người gọi giải phóng. –

+0

@JamesMcNellis: true cho các đối tượng. Nhưng một 'char *' không chính xác là một đối tượng, chỉ là một kiểu C đơn giản. Và trong const const đồng bằng thường được sử dụng để chỉ ra rằng "bạn không sở hữu bộ nhớ này" (vì 'miễn phí (3)' có một con trỏ không const). – vanza

+0

@JamesMcNellis: ví dụ về các chức năng như vậy là gì? Tôi muốn xem xét nó rất khó hiểu và sẽ bình luận rất rõ ràng khi đối tượng được trỏ tới của một con trỏ 'const' được trả về cần thiết để được người gọi giải phóng. – leftaroundabout

2

s2 sẽ có giá trị miễn là s vẫn nằm trong phạm vi. Đó là một con trỏ đến bộ nhớ mà s sở hữu. Xem ví dụ this MSDN documentation: "chuỗi có thời lượng giới hạn và được sở hữu bởi chuỗi lớp".

Nếu bạn muốn sử dụng std::string bên trong một chức năng làm nhà máy để thao tác chuỗi và sau đó trả về chuỗi kiểu c, bạn phải phân bổ bộ nhớ heap cho giá trị trả về. Nhận dung lượng bằng cách sử dụng malloc hoặc new và sau đó sao chép nội dung của s.c_str().

1

S2 có được cấp phát trên ngăn xếp hoặc trong heap không?

Có thể bằng cả hai. Ví dụ: nếu lớp std::string tối ưu hóa chuỗi nhỏ, dữ liệu sẽ nằm trên ngăn xếp nếu kích thước của nó thấp hơn ngưỡng SSO và trên vùng khác. (Và điều này là tất cả giả định rằng đối tượng std::string chính là trên ngăn xếp.)

Tôi có cần xóa s2 không?

Không, đối tượng mảng ký tự được trả về bởi c_str được sở hữu bởi đối tượng chuỗi.

Bây giờ s2 có nằm trên heap không, vì nguồn gốc của nó nằm trên heap?

Trong trường hợp này, dữ liệu có thể sẽ nằm trong heap, ngay cả khi thực hiện SSO. Nhưng hiếm khi có lý do để phân bổ động đối tượng std::string.

+0

Thankyou cho câu trả lời rất chi tiết :) –

4

Thứ nhất, ngay cả chuỗi gốc của bạn không được phân bổ trên ngăn xếp, như bạn có vẻ tin. Ít nhất là không hoàn toàn. Nếu số string s của bạn được khai báo dưới dạng biến cục bộ, chỉ có đối tượng string được phân bổ trên ngăn xếp ". Chuỗi điều khiển của đối tượng chuỗi đó được cấp phát ở một nơi khác. Bạn không được phép biết nó được cấp phát ở đâu, nhưng trong hầu hết các trường hợp, nó được cấp phát trên heap. I E. chuỗi thực tế "Hello world" được lưu trữ bởi s trong ví dụ đầu tiên của bạn thường được phân bổ trên heap, bất kể nơi bạn khai báo s.

Thứ hai, về c_str().

Trong đặc tả ban đầu của C++ (C++ 98) c_str thường trả về một con trỏ tới bộ đệm độc lập được cấp phát ở đâu đó. Một lần nữa, bạn không được phép biết nơi nó được phân bổ, nhưng trong trường hợp chung nó được cho là được cấp phát trên heap. Hầu hết các triển khai của std::string đều đảm bảo rằng chuỗi được kiểm soát của chúng luôn bị ngắt kết nối, vì vậy, c_str trả về một con trỏ trực tiếp đến chuỗi được kiểm soát.

Trong đặc điểm kỹ thuật mới của C++ (C++ 11), yêu cầu c_str trả về một con trỏ trực tiếp đến chuỗi được kiểm soát.

Nói cách khác, trong trường hợp tổng quát, kết quả của c_str sẽ trỏ đến bộ nhớ phân bổ heap ngay cả đối với đối tượng địa phương std::string. Ví dụ đầu tiên của bạn không phải là duifferent từ ví dụ thứ hai của bạn trong vấn đề đó. Tuy nhiên, trong mọi trường hợp, bộ nhớ được chỉ định bởi c_str() không thuộc sở hữu của bạn. Bạn không được phép giải quyết nó. Bạn không được phép biết ngay cả nơi nó được phân bổ.

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