My program has following code:
function FooBar(const s: string): string;
var
sa: AnsiString;
begin
// ..........................
sa := AnsiString(s);
sa := AnsiString(StringReplace(string(sa), '*', '=', [rfReplaceAll]));
sa := AnsiString(StringReplace(string(sa), ' ', '+', [rfReplaceAll]));
result := string(sa);
// ..........................
end;
I noticed that the program did crash "somewhere" and FastMM4 said that I had written to a freed object. As soon as I have commented out "const", the program did work.
I have read the Delphi documentation about const arguments, but I can't figure out why the const argument crashes the program. I would love to understand it.
UPDATE: The program does only crash in Delphi 6 and only if optimization is ON. If the optimization is OFF, the program will work normally. Might it be a Delphi bug?
There are a few peculiar gotchas when it comes to const string parameters.
Many years ago I helped a colleague resolve a similar peculiar problem (D3 iirc). The following simplified example doesn't look like your specific issue, but it may give you some ideas:
type
TMyClass
FString: string;
procedure AppendString(const S: string);
end;
procedure TMyClass.AppendString;
begin
FString := FString + S;
end;
Now if you have an instance of TMyClass and try to call AppendString(FString); to double up the string, you may get an access violation. (There are a few other factors that can affect if you do.) The reason is as follows:
The const prevents refcounting the string on the method call.
So FString may have refCount = 1 when its value is changed.
In which case Copy-on-Write doesn't apply and the string is reallocated. (Most likely at a different address.)
So when the method returns, S is referring to an invalid address, and triggers an AV.
For this case:
sa := s;
automatic reference counting (ARC) works. This is idiomatic way, compiler know how to work with these string - if sa will change, it creates new copy and so on.
For case of hard typecasting (though type is the same)
sa := AnsiString(s);
you tell compiler that you want to get only pointer to string, and you know how to work with this string reference. Compiler will not interfere and annoy you, but you are responsible for correct operations
P.S. I can not reproduce the problem with Delphi XE5 - both simple assigning and typecasting causes LStrLAsg (internal function) call with ARC. (Of course, compiler magic could be changed a bit)
We debugged a crash today that was a Delphi 5 compiler code-gen bug:
procedure TForm1.Button1Click(Sender: TObject);
var
s: string;
begin
s := 'Hello, world! '+IntToStr(7);
DoSomething(s);
//String s now has a reference count of zero, and has already been freed
ShowMessage(s);
end;
procedure TForm1.DoSomething(const Title: string);
var
s: AnsiString;
begin
s := AnsiString(Title);
if Now = 7 then
OutputDebugString('This is a string that is irrelevant');
end;
The Delphi 5 compiler mistakenly tries to decrement the reference counts of both
Title
s
and causes the const string's reference count to go to zero. This causes it to be freed. So when DoSomething returns, you are using a freed string.
An access violation waiting to happen.
Or, in the case of customers: happening.
That becouse AnsiString is a pointer. So, you could'n operate it as a usual strings (which mean a array of chars). Crash is random, so you can gain the crash as result of stackoverflow or access violations at any time, not depend on optimization.
Than you function FooBar return the result, sa variable are already free from mamory.
Try to use the PChar or PAnsiChar and allocate the memory for this variables as your needs.
Since it doesn't make sense to convert to AnsiString and back here, I would rewrite this as
function FooBar(const s:string):string;
begin
Result:=
StringReplace(
StringReplace(
s
,'*','=',[rfReplaceAll])
,' ','+',[rfReplaceAll]);
end;
Related
In this example, in a server service (live 24/24 hours), do I need to free/shrink aBaz for free clean/memory?
function foo(const aBar : string) : boolean;
var
aBaz : string;
begin
aBaz := 'very very long string';
Result := (aBar = aBaz);
aBaz := ''; // shrink `aBaz` for free memory
end;
Update
string var in a class is already free by class destroy or I need to shrink??
Eg.:
class Foo = class
FBar : string;
public
constructor Create; overload;
destructor Destroy; reintroduce;
end;
constructor Foo.Create(const ABar : string);
begin
FBar := ABar;
end;
destructor Foo.Destroy;
begin
FBar := ''; // destructor already free memory or I need to shrink?
end;
in a service (live 24/24 hours) when the run-time library frees the memory?
No, there is no need to free or shrink your string. Two reasons:
This particular string is a string literal. It's not allocated on the heap. The compiler includes a literal copy of that string in your EXE, and when you assign it to aBaz, the variable refers directly to that read-only memory in your EXE file. Nothing gets allocated, so there's nothing to free.
Strings in general are subject to automatic reference counting. When a string variable goes out of scope (which is what happens to aBaz when you reach the end keyword in this function), the string that the variable refers to has its reference count decremented. If the resulting count is zero, then the run-time library frees the memory associated with the string.
The compiler inserts reference-managing code automatically. You don't need to do anything with it.
No, you don't need to free strings on your own. Strings are managed automatically.
aBaz will be automatically freed when foo has finished. The string value will be removed from memory in case there is no other string variable containing the same value.
I am trying to understand how are pointers to strings working. I have a code (not exactly original), which was written by somebody, and the person is not around here anymore, so I need to understand the idea of such usage.
var
STR: string;
pStr: ^string;
begin
STR := 'Hello world';
New(pStr);
pStr^ := STR;
PostMessage(Handle, WM_USER+1, wParam(pStr), 0);
end;
Now I know for sure, that a message handler gets the message and the pointer contains the string, which can be worked with, but what happens 'under the hood' of those operations ?
I tried to make a small project. I thought, that assigning string to what a str pointer is pointing to would actually increase refcount of the original string and not make any copies of a string, but refcount remained 1 and it seems it did copy the contents.
So get the question, what happened? Calling New on a pointer allocates an empty string, right?
After assignment I tried to look at refcount/length of a string the pointer pointed to like this PChar(#pStr^[1])[-8] but it returned nonsense (14), and the length byte was also wrong.
Additionally the questioin is, is it safe using pointers in such a way to pass on the string through windows messaging?
New(pStr) allocates a string on the heap and returns a pointer to it. Because string is a managed type, the string is default initialized, to the empty string. Since a string is implemented as a pointer, what you fundamentally have is a pointer to a pointer.
You code is perfectly fine, so long as you only post the message to your own process. Since the payload of the message is a pointer, it only means something in the context of the virtual address space of your process. If you wanted to send to a different process you'd need an IPC mechanism.
Clearly in the code that pulls the message off the queue you need to dispose of the string. Something like this:
var
p: ^string;
str: string;
....
p := Pointer(wParam);
str := p^;
Dispose(p);
Your code to query the reference count and the length is just wrong. Here's how to do it correctly:
{$APPTYPE CONSOLE}
var
pStr: ^string;
p: PInteger;
begin
New(pStr);
pStr^ := 'Hello world';
p := PInteger(pStr^);
dec(p);
Writeln(p^); // length
dec(p);
Writeln(p^); // ref count
Readln;
end.
Output:
11
1
The given code which works without any problems in Delphi 2007. However in Delphi 2009 I am getting an exception.
Access violation shows read of address $00000000.
The problem exists only when assigning string, it works for numbers.
Also, when I am assigning Data.Text manually via the debugger options I am getting no AV - it works.
Honestly I am lost, anyone could help me with this please?
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, VirtualTrees, StdCtrls;
type
TTest = record
Text: String;
Number: Integer;
end;
PTest = ^TTest;
type
TTestArray = array of TTest;
type
TForm1 = class(TForm)
VirtualStringTree1: TVirtualStringTree;
Button1: TButton;
procedure FormCreate(Sender: TObject);
procedure VirtualStringTree1InitNode(Sender: TBaseVirtualTree; ParentNode,
Node: PVirtualNode; var InitialStates: TVirtualNodeInitStates);
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
TestArray: array of TTest;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
begin
SetLength(TestArray, 1);
TestArray[0].Text := 'test';
TestArray[0].Number := 12345;
VirtualStringTree1.AddChild(VirtualStringTree1.RootNode, #TestArray[0]);
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
VirtualStringTree1.NodeDataSize := SizeOf(TTest);
end;
procedure TForm1.VirtualStringTree1InitNode(Sender: TBaseVirtualTree;
ParentNode, Node: PVirtualNode; var InitialStates: TVirtualNodeInitStates);
var
Data: PTest;
NodeData: PPointer;
begin
Data := Sender.GetNodeData(Node);
NodeData := Sender.GetNodeData(Node);
Data.Number := PTest(NodeData^)^.Number;
Data.Text := PTest(NodeData^)^.Text; //crash here!
end;
end.
When you call AddChild(..., #TestArray[0]), you're only initializing the first four bytes of the node's data. That's the Text field. The Text field holds a pointer to a TTest structure. It's supposed to hold a string reference.
The GetNodeData function returns a pointer to the node's data. The tree control has allocated a TVirtualNode record, and immediately after that, in consecutive memory, it has allocated NodeDataSize bytes for you to use, and GetNodeData returns the address of that space. You're supposed to treat that as a pointer to a TTest structure. And you do, for some of your code. It looks like you're trying to skirt the limitation that only the first four bytes of the structure get initialized when you call AddChild. (I can't say I recommend that. There are other ways to associate data with a node that don't require so much type punning.)
You assign Data correctly for the way the node data is supposed to be used. You assign NodeData correctly for what it really holds at the time of initialization — a pointer to a pointer to a TTest structure. You correctly dereference NodeData to read the Number field, and you also read the Text field correctly. However, the Data.Text field can't be overwritten the way you have it:
Data.Text := PTest(NodeData^)^.Text;
The Data.Text field doesn't current hold a valid string value, but string variables are required to hold valid values at all times (or at least all times where there's a possibility they'll be read or written). To assign a string variable, the program increments the reference count of the new value and decrements the reference count of the old one, but since the "old value" in this case isn't really a string, there's no valid reference count to decrement, and even if there were, the memory at that location couldn't be freed anyway — it belongs to TestArray.
There's a way around this, though. Copy the string in two steps. First, read the value from NodeData.Text into a spare string variable. Once you do that, you have no need for NodeData anymore, so you can overwrite the value it points to. If you set it to all-bits-zero, then you'll implicitly overwrite Data.Text as well, and with the value of an empty string. At that point, it's safe to overwrite as a string variable:
tmp := PTest(NodeData^)^.Text;
PTest(NodeData^) := nil;
Data.Text := tmp;
Another way around this is to re-arrange the order of the fields in the node data. Put the Integer field first, and the initialize Data.Number last instead of Data.Text. Integer values are always safe to overwrite, no matter their contents.
Whatever you do, make sure you finalize the record in the OnFreeNode event:
var
Data: PTest;
begin
Data := Sender.GetNodeData;
Finalize(Data^);
end;
That makes sure the string field gets its reference count reduced, if necessary.
You're missing the point here. You have already inited your node on button's click event, so there is no need to use OnInitNode to init it additionally. What you need is probably use OnGetText to display your data. E.g.:
procedure TForm1.VirtualStringTree1GetText(Sender: TBaseVirtualTree; Node: PVirtualNode;
Column: TColumnIndex; TextType: TVSTTextType; var CellText: string);
var
Data: PTest;
begin
Data := PTest(Sender.GetNodeData(Node)^);
CellText := Data.Text;
end;
With FastMM4 one can easily register a leaked pointer, but not a leaked string. Apparently the # operator applied to a string is not really giving us the whole string, nor is PChar(string); What can I use to nicely register a leaked string?
For now I found this works:
FastMM4.RegisterExpectedMemoryLeak(Pointer(NativeInt(PChar(StringVariable))-12));
But it relies on the magic number 12, and that's version-dependent, and the code really doesn't express what's going on. I hope there's a RTL function somewhere that takes a string and gives back a pointer to the "base" of the string, or some FastMM4 method that I overlooked.
I can pack that ugly beast of expression in a procedure like this, but I still find it hacky:
procedure RegisterExpectedStringLeak(const s:string);
begin
{$IFDEF VER210}
FastMM4.RegisterExpectedMemoryLeak(Pointer(NativeInt(PChar(s))-12));
{$ELSE}
{$MESSAGE Fatal 'This only works on Delphi 2010'}
{$ENDIF}
end;
This should be irrelevant for the question. Here's why I'm leaking strings:
I'm using a caching mechanism to store certain pieces of data for the life of the application. I do not intend to free those objects, because I do need them for the life of the application and going through proper finalization only waists time at application shutdown. Those objects contain some string fields, so obviously those strings are "leaked".
The closest I can think of would be:
function RegisterExpectedStringLeak(const S: string): Boolean;
type
{Have to redeclare StrRec here, because it is not in the interface section of system.pas}
PStrRec = ^StrRec;
StrRec = packed record
{$ifdef CONDITIONALEXPRESSIONS}
{$if RTLVersion >= 20}
codePage: Word;
elemSize: Word;
{$ifend}
{$endif}
refCnt: Longint;
length: Longint;
end;
begin
Result := RegisterExpectedMemoryLeak(Pointer(NativeInt(PChar(S)) - SizeOf(StrRec)));
end;
The re-declaration of StrRec is copied from Delphi XE's getmem.inc (FastMM4) - I only added the {$ifdef CONDITIONALEXPRESSIONS}. I think it should be backwards-compatible with older versions of Delphi, too.
I've got a almost completed app now and the next feature I want to implement is threading. I chose to go with BeginThread(), although am aware of TThread in delphi. The problem I'm coming across is the structure of BeginThread() call. Normally the line in the program that would call the function I want to be threaded is
CompareFiles(form1.Edit3.Text,Form1.Edit4.Text,Form1.StringGrid2,op);
op is a integer.
The line I've switched it out for to create a thread from it is
BeginThread(nil,0,CompareFiles,Addr('form1.Edit3.Text,Form1.Edit4.Text,Form1.StringGrid2,op'),0,x);
From the little amount of infromation I can find on how to actually use BeginThread() this should be a fine call, however on compling all I get is complier errors regarding the structure of my BeginThread() statement paramenters.
EDIT FOR INFORMATION.
The current procedure that calls CompareFiles is
procedure TForm1.Panel29Click(Sender: TObject);
var
op,x : integer;
begin
if (Form1.Edit3.Text <> '') AND (Form1.Edit4.Text <> '') then
begin
op := 3;
if RadioButton7.Checked = True then op := 0;
if RadioButton3.Checked = True then op := 1;
if RadioButton4.Checked = True then op := 2;
if RadioButton5.Checked = True then op := 3;
if RadioButton6.Checked = True then op := 4;
CompareFiles(form1.Edit3.Text,Form1.Edit4.Text,Form1.StringGrid2,op);
end;
end;
If I was to use TThread as suggested by a couple of people, and as displayed by Rob below, I'm confused at how a) I would pass op,Edit3/4.Text and StringGrid2 to the CompareFiles. Guessing from the example of TThread I've seen I thought I would replace the code above with TCompareFilesThread.Executeand the put the current code from Panel29Click into TCompareFilesThread.Create and then add
FEdit3Text := Edit3Text;
FEdit4Text := Edit4Text;
FGrid := Grid;
to this
FEdit3Text := Form1.Edit3.Text;
FEdit4Text := Form1.Edit4.Text;
FGrid := Form1.StringGrid2;
But I've got this nagging feeling that is totally off the mark.
That's not at all the way to use BeginThread. That function expects a pointer to a function that takes one parameter, but the function you're trying to call wants four. The one parameter you're giving to BeginThread for it to forward to the thread procedure is a string, but you evidently hope that some sort of magic will turn that string of characters into the values that those variables contain.
That's not how Delphi works, and even for the languages that can do something like that, it's generally discouraged to actually do it.
To pass multiple parameters to BeginThread, define a record with all the values you'll need, and also define a record pointer:
type
PCompareFilesParams = ^TCompareFilesParams;
TCompareFilesParams = record
Edit3Text,
Edit4Text: string;
Grid: TStringGrid;
Op: Integer;
end;
Change CompareFiles to accept a pointer to that record:
function CompareFiles(Params: PCompareFilesParams): Integer;
To start the thread, you'll need to allocate an instance of that record and populate its fields:
var
Params: PCompareFilesParams;
begin
New(Params);
Params.Edit3Text := Edit3.Text;
Params.Edit4Text := Edit4.Text;
Params.Grid := StringGrid2;
Params.Op := op;
BeginThread(nil, 0, #CompareFiles, Params, 0, x);
Implement CompareFiles like this so that the record will get freed before the thread terminates:
function CompareFiles(Params: PCompareFilesParams): Integer;
begin
try
// <Normal implementation goes here.>
finally
Dispose(Params);
end;
end;
You can make it all a lot easier if you just use TThread, though. You can make your descendant class have as many parameters as you want in its constructor, so you don't have to mess around with dynamically allocating and freeing a special record.
type
TCompareFilesThread = class(TThread)
private
FEdit3Text,
FEdit4Text: string;
FGrid: TStringGrid;
FOp: Integer;
procedure Execute; override;
public
constructor Create(const Edit3Text, Edit4Text: string; Grid: TStringGrid; Op: Integer);
property ReturnValue;
end;
constructor TCompareFilesThread.Create;
begin
inherited Create(False);
FEdit3Text := Edit3Text;
FEdit4Text := Edit4Text;
FGrid := Grid;
FOp := Op;
end;
procedure TCompareFilesThread.Execute;
begin
ReturnValue := CompareFiles(FEdit3Text, FEdit4Text, FGrid, FOp);
end;
Instead of calling BeginThread, you just instantiate the class and let it run:
var
ThreadRef: TThread;
ThreadRef := TCompareFilesThread.Create(Edit3.Text, Edit4.Text, StringGrid2, Op);
There's more to using threads, such as knowing when the thread has finished running, but I think you have enough to get started. One last thing to beware of, though, is that TStringGrid is a VCL control. You mustn't do anything with it from this new thread you create (regardless of how you end up creating it). Eveything you do with the grid control need to be done from the main thread. Use TThread.Synchronize and TThread.Queue to shift any VCL operations onto the main thread. Your file-comparing thread will wait for the synchronized operation to complete, but it will keep running without waiting for a queued operation to complete.