2014-11-15 11 views
8

Có cách nào (trong C thuần khiết) để phân biệt một chuỗi được biên dịch malloc từ một chuỗi ký tự, mà không biết đó là chuỗi nào không? Nói một cách nghiêm túc, tôi đang cố gắng tìm cách kiểm tra một biến dù đó là một chuỗi ký tự hay không và nếu có, tôi sẽ giải phóng nó; nếu không, tôi sẽ để nó đi.Làm thế nào để phân biệt một chuỗi được ghép từ một chuỗi chữ?

Tất nhiên, tôi có thể khai thác mã ngược và chắc chắn rằng nếu biến là malloc ed hay không, nhưng chỉ trong trường hợp nếu một cách dễ dàng tồn tại ...

chỉnh sửa: dòng thêm vào làm cho câu hỏi nữa riêng.

char *s1 = "1234567890"; // string literal 
char *s2 = strdup("1234567890"); // malloced string 
char *s3; 
... 
if (someVar > someVal) { 
    s3 = s1; 
} else { 
    s3 = s2; 
} 
// ... 
// after many, many lines of code an algorithmic branches... 
// now I lost track of s3: is it assigned to s1 or s2? 
// if it was assigned to s2, it needs to be freed; 
// if not, freeing a string literal will pop an error 
+9

Không theo bất kỳ cách di động nào, không. Tại sao bạn cần phải anyway? Có vẻ như bạn đang cố gắng quản lý bộ nhớ một cách sai lầm. Có những cách tốt hơn để đảm bảo rằng bộ nhớ được phân bổ và làm sạch đúng cách. Bước đầu tiên là không "quên" cách phân bổ cái gì đó. –

+0

Tôi không phải là lập trình viên C/C++, nhưng điều này có vẻ khá rõ ràng vì vậy có lẽ tôi đang thiếu thứ gì đó. Dù sao, nếu bạn cần giải phóng 's2', bạn có quan tâm đến' s3' nếu nó được gán giá trị 's1' hoặc' s2', và không có gì khác? Bạn sẽ không giải phóng chuỗi được trả về bởi 'strdup' hai lần, vì vậy nếu bạn định giải phóng' s2', tại sao bạn lại quan tâm đến 's3'? –

+2

Khi bạn viết mã trong C bạn làm như vậy trong khi đảm bảo mạnh mẽ về "ai" sở hữu bộ nhớ. Người gọi có sở hữu nó không? Sau đó, đó là trách nhiệm của họ để deallocate nó. Callee có sở hữu nó không? Điều tương tự. Bạn viết mã rất rõ ràng về chuỗi quyền sở hữu và quyền sở hữu và bạn không gặp phải các vấn đề như "ai đã giải quyết vấn đề này?" Và, như Lasse nói, nếu bạn gọi 'free' trên' s2' * và * 's3' bạn kết thúc với hành vi không xác định. –

Trả lời

8

Có cách nào (trong C tinh khiết) để phân biệt một chuỗi malloced từ một chuỗi chữ,

Không có trong bất kỳ cách nào di động, không có. Không cần phải lo lắng; có những lựa chọn thay thế tốt hơn.

Khi bạn viết mã trong C, bạn làm như vậy trong khi đảm bảo mạnh mẽ về "ai" sở hữu bộ nhớ. Người gọi có sở hữu nó không? Sau đó, đó là trách nhiệm của họ để deallocate nó. Callee có sở hữu nó không? Điều tương tự.

Bạn viết mã rất rõ ràng về chuỗi quyền sở hữu và quyền sở hữu và bạn không gặp phải các vấn đề như "người giải quyết vấn đề này?" Bạn không nên cảm thấy cần phải nói:

// after many, many lines of code an algorithmic branches... 
// now I forgot about s3: was it assigned to s1 or s2? 

Giải pháp là; đừng quên! Bạn kiểm soát mã của mình, chỉ cần tra cứu trang một chút. Thiết kế nó để chống đạn để rò rỉ bộ nhớ ra các chức năng khác mà không hiểu rõ "hey, bạn có thể đọc điều này, nhưng không có gì đảm bảo rằng nó sẽ hợp lệ sau X hoặc Y. Đó không phải là bộ nhớ của bạn, coi nó như vậy . "

Hoặc có thể đó là bộ nhớ của bạn. Trường hợp tại điểm; cuộc gọi của bạn đến strdup. strdup cho bạn biết (thông qua tài liệu) rằng đó là trách nhiệm của bạn để deallocate chuỗi mà nó trả về cho bạn. Làm thế nào bạn làm điều đó là tùy thuộc vào bạn, nhưng đặt cược tốt nhất của bạn là hạn chế phạm vi của nó là hẹp nhất có thể và chỉ giữ nó trong khoảng thời gian ngắn khi cần thiết.

Cần có thời gian và thực hành để điều này trở thành bản chất thứ hai. Bạn sẽ tạo một số dự án xử lý bộ nhớ kém trước khi bạn sử dụng nó tốt. Vậy là được rồi; sai lầm của bạn trong đầu sẽ dạy cho bạn chính xác những gì không để làm, và bạn sẽ tránh lặp lại chúng trong tương lai (hy vọng!)

Ngoài ra, như @Lasse ám chỉ trong các ý kiến, bạn không có phải lo lắng về s3, là bản sao của con trỏ, không phải toàn bộ đoạn bộ nhớ. Nếu bạn gọi miễn phí trên s2s3, bạn kết thúc với hành vi không xác định.

+0

Ngoài trường hợp: Giả sử 's1' là một chữ cái &' s2' là một var (như mã nói). Các nhánh mã với 'if' và gán giá trị của' s1' hoặc 's2' thành' s3', tùy thuộc vào một số điều kiện. Sau đó, một hàm foo (s3) được gọi. Bây giờ, nếu tôi cần giải phóng 's3' trong func. 'foo', tôi không còn-bất kỳ đầu mối nào về' s3'. Để chắc chắn, tôi đã vượt qua hai vars khác ('someVar' &' someVal') vào func. 'foo' và ** đánh giá lại ** điều kiện. Chỉ bằng cách truyền các biến khác, tôi có thể chắc chắn về giá trị con trỏ của 's3', cho dù nó được gán cho' s1' hay 's2'. – ssd

+3

@ merkez3110: Vấn đề của bạn không phải là vấn đề mà bạn mô tả. Vấn đề thực sự là bạn đã vi phạm các quy tắc mà tôi đang nói đến. Khi bạn thiết kế mã của mình theo cách như vậy, điều này có thể xảy ra mà bạn đã mất. Bạn tạo ra một hệ thống khó duy trì và lý do. Giải pháp; đừng làm thế. * "Bây giờ, nếu tôi cần giải phóng s3 trong func.' Foo' ... "* - Không, bạn không cần! Bạn đã che mờ ai chịu trách nhiệm cho bộ nhớ đó. nếu 'foo' có thể chấp nhận bất kỳ chuỗi nào * thì nó sẽ không chịu trách nhiệm về deallocation *. –

+2

... Người gọi 'foo' chịu trách nhiệm bởi vì chỉ có nó biết chính xác điều cần làm. Bạn đã đẩy sai trách nhiệm/quyền sở hữu đối với một chức năng không thể lý giải về đầu vào của nó. *Đó là vấn đề. Đây là vấn đề thiết kế, không phải là vấn đề kỹ thuật. Nhìn vào các chức năng thư viện chuẩn; có bao nhiêu chức năng bạn nhìn thấy có một 'char *' và * cũng * deallocate nó? –

0

Dưới đây là một cách thực hiện:

Mặc dù C-ngôn ngữ tiêu chuẩn không lệnh này, cho tất cả các lần xuất hiện giống hệt nhau của một chuỗi chữ được đưa ra trong mã của bạn, trình biên dịch sẽ tạo ra một bản sao duy nhất trong RO- phần dữ liệu của hình ảnh thực thi.

Nói cách khác, mọi lần xuất hiện của chuỗi "1234567890" trong mã của bạn được dịch sang cùng một địa chỉ bộ nhớ.

Vì vậy, tại điểm mà bạn muốn deallocate chuỗi, bạn có thể chỉ đơn giản là so sánh địa chỉ của nó với địa chỉ của chuỗi literal "1234567890":

if (s3 != "1234567890") // address comparison 
    free(s3); 

Để nhấn mạnh điều này một lần nữa, nó không phải là áp đặt bởi tiêu chuẩn ngôn ngữ C, nhưng nó được thực thi bởi bất kỳ trình biên dịch nào của ngôn ngữ.


UPDATE:

Trên đây chỉ là một thủ thuật thực tiễn đề cập trực tiếp đến vấn đề ở bàn tay (chứ không phải là động lực đằng sau câu hỏi này). Nói một cách chính xác, nếu bạn đã đạt được điểm trong việc triển khai, bạn phải phân biệt giữa chuỗi được phân bổ tĩnh và chuỗi được phân bổ động, thì tôi có xu hướng đoán rằng thiết kế ban đầu của bạn là thiếu sót ở đâu đó dọc theo dòng.

+0

Một "kỹ thuật thực tế" khác có thể là để tìm ra phần không gian địa chỉ mà hệ thống của bạn sử dụng cho heap và xem chuỗi có nằm trong đó hay không. –

+0

@MattMcNabb: Tôi cũng nghĩ về điều này, nhưng không thể nghĩ ra cách thể hiện nó bằng mã. Tùy chọn duy nhất được bảo đảm để hoạt động là tìm kiếm trong cài đặt trình liên kết của bạn (hoặc tệp cấu hình). –

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