2009-07-15 42 views
10

Tôi đã nói chuyện với một đồng nghiệp ngày khác về cách bạn có thể rò rỉ một chuỗi trong Delphi nếu bạn thực sự mess điều lên. Theo mặc định, chuỗi được tính tham chiếu và được phân bổ tự động, vì vậy chúng thường chỉ hoạt động mà không cần suy nghĩ - không cần phân bổ thủ công, tính toán kích thước hoặc quản lý bộ nhớ.Làm thế nào để rò rỉ một chuỗi trong Delphi

Nhưng tôi nhớ đọc một lần rằng có một cách để rò rỉ một chuỗi trực tiếp (không bao gồm nó trong một đối tượng bị rò rỉ). Dường như nó có liên quan đến việc truyền một chuỗi bằng cách tham chiếu và sau đó truy cập nó từ một phạm vi lớn hơn từ bên trong thường trình mà nó được chuyển đến. Vâng, tôi biết điều đó là mơ hồ, đó là lý do tại sao tôi hỏi câu hỏi ở đây.

Trả lời

2

Trên thực tế, thông qua chuỗi như CONST hoặc không const đều giống nhau về mặt số lượng tài liệu tham khảo trong Delphi 2007 và 2009. Đã xảy ra trường hợp gây ra vi phạm truy cập khi chuỗi được chuyển thành CONST. Đây là vấn đề một trong số

type 
    TFoo = class 
    S: string; 
    procedure Foo(const S1: string); 
    end; 

procedure TFoo.Foo(const S1: string); 
begin 
    S:= S1; //access violation 
end; 

var 
    F: TFoo; 
begin 
    F:= TFoo.create; 
    try 
    F.S := 'S'; 
    F.Foo(F.S); 
    finally 
    F.Free; 
    end; 
end. 
+0

Điều đó có vẻ giống như tôi đang nghĩ, nhưng nó không phải AV trong năm 2009, mà tôi tin là những gì bạn đang nói. –

+3

Nó không AV vì trong Delphi 2009, "const" mất chức năng của nó nếu $ STRINGCHECKS được BẬT. –

7

Tôi không biết về vấn đề trong đoạn thứ hai của bạn, nhưng tôi bị cắn một lần bởi các chuỗi bị rò rỉ trong hồ sơ.

Nếu bạn gọi FillChar() trên bản ghi chứa chuỗi bạn ghi đè số đếm và địa chỉ của bộ nhớ được cấp phát động bằng số không. Trừ khi chuỗi rỗng, điều này sẽ làm rò rỉ bộ nhớ. Cách này là gọi Hoàn thành() trên hồ sơ trước khi xóa bộ nhớ mà nó chiếm.

Thật không may gọi số Hoàn tất() khi không có thành viên ghi nào cần hoàn thành gây ra gợi ý trình biên dịch. Đã xảy ra với tôi rằng tôi đã nhận xét cuộc gọi Hoàn thành() để tắt tiếng gợi ý, nhưng sau đó khi tôi thêm thành viên chuỗi vào bản ghi, tôi đã bỏ lỡ cuộc gọi, vì vậy đã bị rò rỉ. May mắn thay, tôi thường sử dụng trình quản lý bộ nhớ FastMM trong cài đặt tiết kiệm và hoang tưởng nhất trong chế độ gỡ lỗi, vì vậy sự rò rỉ không bị bỏ qua.

Gợi ý trình biên dịch có lẽ không phải là điều tốt, âm thầm bỏ qua cuộc gọi Hoàn thành() nếu không cần thiết sẽ là IMHO tốt hơn nhiều.

+0

> âm thầm bỏ qua cuộc gọi Finalize() nếu không cần thiết sẽ tốt hơn nhiều IMHO +1 –

+0

bạn có biết nếu các chuỗi ngắn bị ảnh hưởng bởi điều này không? –

+0

@ JamesB: Không, chuỗi ngắn không bị ảnh hưởng bởi điều này, vì chúng không được tính tham chiếu và không bao gồm các con trỏ đến các phần bộ nhớ có thể bị mất. Các chuỗi ngắn bao gồm một byte độ dài đơn và dữ liệu ký tự, do đó, điền chúng bằng '0' chỉ đặt độ dài thành '0' và đặt tất cả các phần tử thành' # 0'. – mghie

4

Không, tôi không nghĩ một điều như vậy có thể xảy ra. Có thể cho một biến chuỗi để có được một giá trị mà bạn không mong đợi, nhưng nó sẽ không bị rò rỉ bộ nhớ. Hãy xem xét điều này:

var 
    Global: string; 

procedure One(const Arg: string); 
begin 
    Global := ''; 

    // Oops. This is an invalid reference now. Arg points to 
    // what Global used to refer to, which isn't there anymore. 
    writeln(Arg); 
end; 

procedure Two; 
begin 
    Global := 'foo'; 
    UniqueString(Global); 
    One(Global); 
    Assert(Global = 'foo', 'Uh-oh. The argument isn''t really const?'); 
end; 

Đây là đối số được tuyên bố là const, vì vậy được cho là sẽ không thay đổi. Nhưng sau đó One circumvents rằng bằng cách thay đổi các tham số thực tế thay vì tham số chính thức. Thủ tục Two "biết" rằng đối số của One là const, do đó, nó hy vọng tham số thực tế để giữ lại giá trị ban đầu của nó. Xác nhận không thành công.

Chuỗi không bị rò rỉ, nhưng mã này không cho biết cách bạn có thể nhận được một tham chiếu dang lửng cho một chuỗi. Arg là bí danh địa phương của Global. Mặc dù chúng tôi đã thay đổi Global, giá trị của Arg vẫn không bị ảnh hưởng và bởi vì nó được khai báo const, số lượng tham chiếu của chuỗi không tăng lên khi nhập vào hàm. Chỉ định lại Global đã giảm số tham chiếu về 0 và chuỗi đã bị hủy. Khai báo Arg vì var sẽ có cùng một vấn đề; chuyển nó theo giá trị sẽ khắc phục vấn đề này. (Gọi tới UniqueString chỉ để đảm bảo chuỗi được tính tham chiếu. Nếu không, nó có thể là một chuỗi ký tự không được tham chiếu.) Tất cả các loại do trình biên dịch quản lý đều dễ bị vấn đề này; loại đơn giản là miễn dịch.

Cách duy nhất để làm rò rỉ chuỗi là xử lý nó dưới dạng chuỗi khác, hoặc sử dụng các chức năng quản lý bộ nhớ không nhận dạng.Mghie's answer mô tả cách xử lý chuỗi dưới dạng chuỗi khác bằng cách sử dụng FillChar để nén một biến chuỗi. Các chức năng bộ nhớ không nhận dạng bao gồm GetMemFreeMem. Ví dụ:

type 
    PRec = ^TRec; 
    TRec = record 
    field: string; 
    end; 

var 
    Rec: PRec; 
begin 
    GetMem(Rec, SizeOf(Rec^)); 
    // Oops. Rec^ is uninitialized. This assignment isn't safe. 
    Rec^.field := IntToStr(4); 
    // Even if the assignment were OK, FreeMem would leak the string. 
    FreeMem(Rec); 
end; 

Có hai cách để khắc phục. Một là để gọi InitializeFinalize:

GetMem(Rec, SizeOf(Rec^)); 
Initialize(Rec^); 
Rec^.field := IntToStr(4); 
Finalize(Rec^); 
FreeMem(Rec); 

Các khác là sử dụng chức năng loại-aware:

New(Rec); 
Rec^.field := IntToStr(4); 
Dispose(Rec); 
+1

AFAICS ví dụ toàn cầu hoạt động cho bất kỳ loại biến nào, vì vậy nó có thể không phải là những gì Jim là sau. –

+1

Ah, bạn nói đúng. Nhưng như một chuỗi, nó * có thể * gây ra các vấn đề khác, khi tôi chuẩn bị chứng minh khi tôi chỉnh sửa câu trả lời này. –

0

Tôi nghĩ rằng this có thể giống với suy nghĩ của tôi. Nó là đảo ngược của một chuỗi bị rò rỉ, một chuỗi được thu thập sớm:

var 
    p : ^String; 

procedure InitString; 
var 
    s, x : String; 
begin 
    s := 'A cool string!'; 
    x := s + '. Append something to make a copy in' + 
      'memory and generate a new string.'; 

    p := @x; 
end; 

begin 
    { Call a function that will generate a string } 
    InitString(); 

    { Write the value of the string (pointed to by p) } 
    WriteLn(p^); // Runtime error 105! 


    { Wait for a key press } 
    ReadLn; 
end. 
+3

Bạn có một con trỏ trỏ đến một đối tượng trên ngăn xếp nằm ngoài phạm vi. Bạn mong chờ điều gì??? Có hay không chuỗi được làm sạch lên điều này có khả năng thổi. –

+2

Loren, bạn * mong đợi * mọi thứ hoạt động chính xác như bạn dự định, bất kể mã thực tế bạn đã viết! Trình biên dịch là tâm linh ngày nay, phải không? –

+0

Gotta đồng ý với Loren ở đây. Thứ hai bạn gọi biểu tượng @, tất cả các phiên cược sẽ bị tắt. Bạn đang đấu thầu trình biên dịch một lời tạm biệt lịch sự với bất kỳ và tất cả các loại an toàn và tham khảo-đếm cơ chế và lấy bộ nhớ nguyên vào tay của riêng bạn. –

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