?
Solved

String to PChar issue

Posted on 2003-03-11
48
Medium Priority
?
987 Views
Last Modified: 2011-09-20
In the example below I need to know how delphi manages reference counting on strings.

//**********************************************************************************
function GetSomeString1 : String;
function GetSomeString2 : String;
function GetSomeString3 : String;

procedure DoSomething;
var
  PChar1, PChar2, PChar3 : PChar;
begin
  PChar1 := PChar (GetSomeString1);
  PChar2 := PChar (GetSomeString2);
  PChar3 := PChar (GetSomeString3);
  DoSomethingMore (PChar1, PChar2, PChar3);
end;
//**************************************************************************

When will the reference count of the string resulting from GetSomeStringX decrease :
Immediately after each call to GetSomeStringX, or at the end of DoSomething?

The question is theoretical. In my case this works anyway because GetSomeStringX return strings buffered in global data structures, so reference counts will not reach zero. I only want to know if this way of typing code, in general, is "good or bad" (my 80% guess is bad...).

regards
classmate.
0
Comment
Question by:classmate
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 18
  • 15
  • 9
  • +2
48 Comments
 
LVL 2

Expert Comment

by:PeterLarsen
ID: 8113821
Hi classmate,

Just a few hints :

function GetSomeString1 : String; return a string (of cource) and this string only exist 'on the stack'. If you want to keep the string you must save it in a var of string (or similar).

You can't just save the pointer to the string, because there isn't any owner.

I understand if you want to save the pointer rather than the entire string because its faster and occupy less memory. But there must be a owner to hold the string.

The function 'function GetSomeString1 : String;' should be 'function GetSomeString1 : PChar;' or Pointer.

Also, when you are working with strings and pointers - remember that the first char in the string is in position 1 (not position 0). See the following sample :

procedure TForm1.Button1Click(Sender: TObject);
var
 s : string;
 p : Pointer;
begin
  S:='123';
//  p:=addr(s[0]); is not ok
  p:=addr(s[1]); //is OK
  Edit1.Text:=pchar(P);
end;

Hope this may help you.
Peter
0
 
LVL 1

Expert Comment

by:SimesA
ID: 8118198
Peter: What you say is true, except "GetSomeStringX return strings buffered in global data". So the string does have an owner.

Classmate: if you want pchars, why not make the GetSomeString return a pchar directly?
0
 
LVL 2

Author Comment

by:classmate
ID: 8118720
Hi experts.
I am looking for excact information about the events of reference counting.

I am a rather experienced programmer, do not underestimate my question.

I will try to clarify it by giving this example.



var
  GlobalString : string = 'Something'; // Reference count := 1;

function StringFunc1 : string;
begin
  result := GlobalString; // Reference count := 2
end;

function StringFunc2 : string;
var
  LocalString : string;
begin
  LocalString = 'Oops';  // reference count := 1
  result := LocalString; // reference count := 2
end;                     // reference count := 1


procedure DoNothing;
begin
  StringFunc1;  
  // What is the reference count of GlobalString now?

  StringFunc2;
  // What is the reference count of LocalString now?

// The resulting strings are not assigned to a variable.
// Yet they are returned from StringFuncX and has to
// initially exist in  memory until the reference count
// is decreased to zero.
// Therefore my my question is:
// At which point does it decrease? Above (immediately
// after the function calls),
// or below (when DoNothing exits)?
// If you are not sure, please admit.
end;
0
VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

 
LVL 2

Author Comment

by:classmate
ID: 8118738
Or to put it this way; When will the resulting references to the strings be removed from memory: immediately after the function calls, or when DoNothing exits?
I am asking about the small piece of memory assigned to the Result variable in each function call, not the actual strings.
0
 
LVL 2

Author Comment

by:classmate
ID: 8118749
Increasing points...
0
 
LVL 2

Expert Comment

by:PeterLarsen
ID: 8119036
I know that the entire string isn't returned on the stack. Its only the pointer to the string that is retuned - i think its in EAX - not sure.

Saving the pointer to a string does not increase the reference-count.
I still believe that the string can be overriden any time after the control has left the functions - because there isn't any owner.

Sample :

Push Button1 - the string are valid.
Push Button2 - the string does no longer exist.

procedure TForm1.Button1Click(Sender: TObject);
begin
  P:=addr(GetString[1]);
  Edit1.Text:='';
  Edit1.Text:=string(P);
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  Edit1.Text:='';
  Edit1.Text:=string(P);
end;

function TForm1.GetString: string;
begin
  Result:='123';
end;

Peter
0
 
LVL 2

Expert Comment

by:PeterLarsen
ID: 8119070
Btw - I don't underestimate you, but i have no chance to now how experienced you are, right :-)
0
 
LVL 2

Author Comment

by:classmate
ID: 8119500
;-)

According to this, it should be OK to do the following?

function MyFunc : String;
begin
  result := '123';
end;

var
  pc : PChar;
begin
  MyWindowsAPIFunc (PChar(MyStringFunc));
  // or
  pc := pchar (MyStringFunc);
  MyWindowsAPIFunc (pc);
end;

The reason it this might work is that there might be an implicit reference to the resulting string through the function call management in delphi, because of the assignment to Result. My suspicion is that Result will exist as an implicit reference until the caller function exits.

If so, then the example above, and also my very first one, do nothing wrong. But if this "implicit reference" is removed directly after its value eventually is assigned to another variable, then the reference count will also count down at the same point of time, and the actual string might be freed from memory, casing errors on PChar typecasts.

What I DO know is that reference down-counting tend to be done automatically at the end point of a procedure or function, if it is not done by code.

My clarified question is:
Does a somewhat implicit reference to the resulting string prevent it from being freed until the caller exits ... or does it not ?

-An ansistring can have many owners because of delphis copy-on-write tecnique, so I am not completely compatible with the term Owner that you tend to use.

0
 
LVL 17

Expert Comment

by:geobul
ID: 8120356
Hi,

Some points that may help (or need to think of):

1.PChar is not reference-counted (as far as I know).

2.When you typecast a string as PChar, this doesn't increment the reference count. The following is from Delphi 5 help:

---
Sometimes you will need convert a long string to a null-terminated string, for example, if you are using a function that takes a PChar. However, because long strings are reference counted, typecasting a string to a PChar increases the dependency on the string by one, without actually incrementing the reference count. When the reference count hits zero, the string will be destroyed, even though there is an extra dependency on it. The cast PChar will also disappear, while the routine you passed it to may still be using it. If you must cast a string to a PChar, be aware that you are responsible for the lifetime of the resulting PChar.
---

3.The result of a function that returns 'string' is the string itself and a pointer to it as one extra (additional) 'var' type parameter passed by the caller routine.


As a result of the above I would say:

1.
...
GetSomeString1;
>at this point the result of the function is freed because the reference counter becomes 0.

2.
...
PChar1 := PChar (GetSomeString1);
>here, because of the assignment, the reference count of the result is 1 and the returned string exists. Typecasting doesn't change that.

3.
function StringFunc1 : string;
begin
 result := GlobalString;
end;

procedure DoNothing;
begin
 StringFunc1;  
>reference count of GlobalString is 1 because there was no assignment. StringFunc1 has returned a string that is not GlobalString but its copy.

4.
function StringFunc2 : string;
var
 LocalString : string;
begin
 LocalString = 'Oops';  // reference count := 1
 result := LocalString; // reference count := 1 because the local string was copied not referenced
end;                     // reference count := 0

StringFunc2;
>LocalString is out of scope and doesn't exist anymore.

Regards, Geo
0
 
LVL 17

Expert Comment

by:geobul
ID: 8120532
I'm sorry but
>StringFunc1 has returned a string that is not GlobalString but its copy.

that I said in my previous post is not correct.

The actual reason is that after the function call the reference counter is decremented (there is no assignment). I think of it this way: assignment increments the counter to the result and the end of the called function decrements it back.

Seems that I messed up this (looking so simple) thing :-)

Regards, Geo
0
 
LVL 2

Expert Comment

by:PeterLarsen
ID: 8120769
Classmate,
Yes that should work - at least until function exit.

if i was doing MyWindowsAPIFunc and the function won't be using the pointer imediately (right spelling ?), i think i would copy the string over for later use.

Geo got a point there - i forgot to asked whether the result returned by "function GetSomeString1 : String;" was global or lokal.
If its declared global (eg. under public) the string already has a (what i here call) a owner - and therefore is valid.

>>Does a somewhat implicit reference to the resulting string prevent it from being freed until the caller exits ... or does it not ?
var
 pc : PChar;
begin
 //this will work 100 %
 MyWindowsAPIFunc (PChar(MyStringFunc));
 // or
 //this could theoretically be a problem - especially in a multi-threading environment.
 pc := pchar (MyStringFunc);
 MyWindowsAPIFunc (pc);
end;

Regards
Peter
0
 
LVL 2

Expert Comment

by:PeterLarsen
ID: 8120797
Geo, what you say is that a result string will always be a copy ?
0
 
LVL 17

Expert Comment

by:geobul
ID: 8121290
Hi Peter,

No, I was wrong about:

3.
function StringFunc1 : string;
begin
result := GlobalString;
end;
...

See my second comment.

I finally found that piece of Delphi help that I was looking for (under 'Function results' title):
---
For a string, dynamic array, method pointer, or Variant result, the effects are the same as if the function result were declared as an additional var parameter following the declared parameters. In other words, the caller passes an additional 32-bit pointer that points to a variable in which to return the function result.
---

About:

var pc: PChar;
begin
 pc := PChar(StringFunc1);
 ..

I think that pc contains a copy of the result. Their types are different and the String type result is copied into a PChar type variable.

Regards, Geo
0
 
LVL 2

Author Comment

by:classmate
ID: 8121339
Well.. seems like there is a lot of confusion about this.

PeterLarsen,
The validity of the result string is of secondary concern to the solution to my question. All I need to know is that simple, but non-obvius detail i ask about: Implicit references. What i actually ask about, is a reference existing in application memory, that are unavailable to the programmer via any variable.

I tend to believe that there is such a reference, originally residing in the Result variable in the CALLED function, that also will exist when the CALLED function has finished. Furthermore I think that this "invisible" reference is cleaned up/finalized when the CALLING function exits.

But this exactly what i am not sure about.


Therefore:
Why will

    MyWindowsAPIFunc (PChar(MyStringFunc));

work 100%, while

    pc := pchar (MyStringFunc);
    MyWindowsAPIFunc (pc);

could represent a problem?



Geobul,
>> assignment increments the counter to the result and the end of the called function decrements it back.

If this were true in general, strings in many cases could be freed before they were assigned to the recieving variable. I tend to believe that the recieving variable is not treated as the same one as Result (like var params), but recieves its value after the CALLED function has exited. Thus the Result reference still must exist after the call, and becomes a "hidden" variable in the CALLING function, which will be cleaned up/finalized at the end of the CALLING function.


Everyone,
If you know for sure that i am wrong about anything, please let me know.

regards Classmate
(originally, 5 years ago when i registered as an expert, i invented this name because i thought i would spend time helping people buliding intelligent classes and objects... I was very wrong, since questions always are technical *LOL*)
0
 
LVL 2

Author Comment

by:classmate
ID: 8121459
geobul, i didn't read your last comment before posting mine. I think I'll try to debug a test project in assembler mode to become sure.

Investigating...
0
 
LVL 2

Expert Comment

by:PeterLarsen
ID: 8121736
Aha, this is new to me.
So the calling function prepare space in memory for the result returned by the called function - which means that the calling function keeps the string until it exit.

>>I tend to believe that the recieving variable is not treated as the same one as Result (like var params), but recieves its value after the CALLED function has exited. Thus the Result reference still must exist after the call, and becomes a "hidden" variable in the CALLING function, which will be cleaned up/finalized at the end of the CALLING function.

I agree with classmate here, except that Result (in the called function) must be a pointer and any data assigned to Result is written imediately and not on exit.
If reference-count exist here i think its controlled by the calling function only.

If this is so, there are no differences between "MyWindowsAPIFunc(PChar(MyStringFunc));" and "pc := pchar(MyStringFunc);/MyWindowsAPIFunc(pc);".

I have just tested this, and the pointers har not the same, so it is a copy :
var
 s : string;

function TForm.GetString : result;
begin Result:=S;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
 p : pointer;
begin
  P:=addr(GetS[1]);
//  P and addr(S[1]) are not the same.
end;

Peter
0
 
LVL 2

Author Comment

by:classmate
ID: 8121848
I have performed a test, and it seems that I am right. This means that the Delphi Help is not completely up to date.

I tried this in a new project:

function test : String;
begin
 result :='ABC'; // implicitly calling _LStrAsg, defined in the System unit
end;

procedure TForm16.Button1Click(Sender: TObject);
begin
  test;
  test;
  test;
 
{
At this invisible point, and only here, one of _LStrClr and _LStrArrayClr, defined in the system unit, is called, to count down reference counts and free strings. If there is only one call to test, _StrClr is called, else _LStrArrayClr is called, looping as many times as function Test has been called, to free as many strings as delphi source code has buffered up.
}

end;


This proves that my theory of "invisible references" is true.
That in turn, proves that any typecast of string function results to PCHar will work fine, as long as PChar memory is not accessed after the calling function has finished.

I am absoulutely sure of this, and that is what i wanted to become. As mentioned early, i wanted to know the exact events of reference downcounting. None of you could come up with an exact answer, but you helped me to conclude, so i am willing to give you 50 points each for your effort.

Any comments?
Did you find this interesting?

Classmate.
0
 
LVL 2

Author Comment

by:classmate
ID: 8121908
the offering of 50 points each is by the way intended only for PeterLarsen and Geobul.
0
 
LVL 17

Expert Comment

by:geobul
ID: 8122496
Hi,

I can't understand what you are after. At what stage a string will actually be dereferenced is a matter of implementation (or optimization probably). I don't think that there would be a problem if the result was dereferenced immediately after the function call. This, at certain point might have an impact on the performance.

Both your ways:

MyWindowsAPIFunc (PChar(MyStringFunc));

and

pc := pchar (MyStringFunc);
MyWindowsAPIFunc (pc);

are correct if MyWindowsAPIFunc's parameter is an input parameter.

About your last comment:

result :='ABC';

is not exactly the same as: result := GlobalString; which we've discussed so far.

What you've found could be an optimization issue. If the result of the first call was deferenced before the second call then:
- first 'ABC' is created
- the first 'ABC' should be freed
- a second 'ABC' must be created (but it's the same as the first one)
etc.

Regards, Geo
0
 
LVL 2

Expert Comment

by:PeterLarsen
ID: 8122621
Dont know what to say :-)
Yes the discussion is interesting - going into details is always fun.

But i dont understand why this is important to you.

1:
If you want Result to be a string, you will also save that string in a var of string.
If you only need the pointer, Result should be a pointer/pchar.
If you follow this, you dont have to worry about reference count.

2:
It doesn't matter what the reference-count are, because Result is unavailable unless you save Result in the calling function.

3:
>>Thus the Result reference still must exist after the call, and becomes a "hidden" variable in the CALLING function..

This is not true. The "hidden" variable already exist in the CALLING function before the CALLED function is called.

4:
>>That in turn, proves that any typecast of string function results to PCHar will work fine, as long as PChar memory is not accessed after the calling function has finished.

-which makes it useless to do so, because the memory may be overriden at any time after exit.

Peter
0
 
LVL 2

Author Comment

by:classmate
ID: 8126798
For PeterLarsen:
3:
>>>Thus the Result reference still must exist after the call, and becomes a "hidden" variable in the CALLING function..

>>This is not true. The "hidden" variable already exist in the CALLING function before the CALLED function is called.

OK that might be true, but the string by itself is not freed in the same function that initiates it, but at the end of the routine that calls it.
The reason why this became interesting, is that I was debugging source code written by someone else. This source code made use of typecasts to PChar to call an external function. The way it was done was obviously wrong, so i corrected it to the better - i thought. But then i came in doubt and started this discussion.
I always think it is a bad idea to make source code with more programming lines than needed, so if a function result of type string can be directly typecasted to PChar for a new function call without the risk that the string first is freed, then it FOR SURE, ALWAYS saves me the job of first assigning the function result to a string variable and afterwards typecast this.


For Geobul:

function GetSomeString : String;
begin
  result := 'whatever';
end;

procedure DoSomeThing
var
  pc : PChar;
begin
  // if i don't get you wrong, you assume that the following line of code increases the reference count.
  pc := PChar(GetSomeString);
end;
 
That is not true. The string recieves a reference count of 1 when it is assigned to result in GetSomeString. After that, nothing happens, until DoSomeThing finishes. At that point reference counts are counted down on all strings (internally buffered by delphi) recieved from any function call. When reference counts are zero the string frees.

This complete mechanism is the reason why function results of type String can be typecasted to PChar variables and used within the scope of the CALLER, but cannot be passed as a result to the CALLERS CALLER still as PChar.

This excact knowledge will maybe in time save me as many programming lines as i have written in this thread :-) Or maybe not. But it still feels nice to not be in doubt about what is allowed and what isn't. In this case i for my part needed the reference counting info to be sure.

classmate.
0
 
LVL 17

Expert Comment

by:geobul
ID: 8128781
I've never said that! I said first:

>>>
2.
...
PChar1 := PChar (GetSomeString1);
>here, because of the assignment, the reference count of the result is 1 and the returned string exists. Typecasting doesn't change that.
<<<

and later:

>>>
About:

var pc: PChar;
begin
pc := PChar(StringFunc1);
..

I think that pc contains a copy of the result. Their types are different and the String type result is copied into a PChar type variable.
<<<

Hope that it's clear enough. Perhaps, my second thought is wrong but can't check that now.

Regards, Geo
0
 
LVL 2

Author Comment

by:classmate
ID: 8129003
Hi geobul, it is the first statement that I seem to have  misunderstood.

(
Written in my way of understanding your statement:
"The fact that a typecast occur, does not change the fact that the assignment of the function result into any variable prevents reference count from decreasing".
This was seemingly a wrong interpretation... sorry ;-(
)

Both: your points are to be posted now, check up new questions...

classmate.
0
 
LVL 20

Expert Comment

by:Madshi
ID: 8141914
Yep, there are kind of invisible references. The problem is can't be sure when they will be freed. Sometimes Delphi frees them in the middle of the calling function, sometimes at the end. So you should not write code which relies on where Delphi frees those invisible references. Instead you should make sure yourself that the variables are not freed too early. So I'd not recommend to use the code which you posted in your original question. You can use:

DoSomethingMore (PChar (GetSomeString1), PChar (GetSomeString2), PChar (GetSomeString3));

But if you need to store the pointers before calling "DoSomethingMore", you should really do it like this:

var s1, s2, s3 : string;
    p1, p2, p3 : pchar;
begin
  s1 := GetSomeString1; p1 := pchar(s1);
  s2 := GetSomeString2; p2 := pchar(s2);
  s3 := GetSomeString3; p3 := pchar(s3);
  DoSomethingMore(p1, p2, p3);

Or this way:

var s1, s2, s3 : string;
begin
  s1 := GetSomeString1;
  s2 := GetSomeString2;
  s3 := GetSomeString3;
  DoSomethingMore(pchar(s1), pchar(s2), pchar(s3));

Regards, Madshi.
0
 
LVL 2

Expert Comment

by:PeterLarsen
ID: 8142081
Hi Madshi - good to hear.

So, we can't be sure when Result is freed in the CALLING function - very interesting - it seems that everything is back to normal again.

0
 
LVL 2

Author Comment

by:classmate
ID: 8150753
Hi Madshi

I thought the - End; - part of a routine is THE only place where delphi puts in code for all kinds of de-referencing (both interfaces, dynamic arrays and strings) ???

And if this is true, won't also
DoSomethingMore (PChar (GetSomeString1), PChar (GetSomeString2), PChar (GetSomeString3));
be wrong if results are de-referenced in the End; of GetSomeStringX?

regards
classmate.
0
 
LVL 20

Accepted Solution

by:
Madshi earned 1200 total points
ID: 8164979
>> I thought the - End; - part of a routine is THE only place where delphi puts in code for all kinds of de-referencing (both interfaces, dynamic arrays and strings) ???

I experienced situations where Delphi dereferenced interfaces instances earlier than at the end of the routine (e.g. inside of a big loop). So *no*, you can not rely on that dereferencing is only done at the end of a function.

>> And if this is true, won't also DoSomethingMore (PChar (GetSomeString1), PChar (GetSomeString2), PChar (GetSomeString3)); be wrong if results are de-referenced in the End; of GetSomeStringX?

The GetSomeStringX results are *directly* used in the call to DoSomethingMore. So Delphi won't free them too early in this case.
0
 
LVL 2

Author Comment

by:classmate
ID: 8165837
Your experience with dereferencing in loops will be worth the points of this question..

However I'd very much like Borland to document that the last part of your comment, which also other experts maintain, IS allowed. I haven't found that kind of documentation yet... :-)

regards
classmate.
0
 
LVL 20

Expert Comment

by:Madshi
ID: 8165877
Yes, some more documentation would be useful.
0
 
LVL 2

Expert Comment

by:PeterLarsen
ID: 8166156
I think that "DoSomethingMore (PChar (GetSomeString1), PChar (GetSomeString2), PChar (GetSomeString3));" is the safest way if you dont save Result in a local var

As you know, a function push the registers on the stack in order to protect the interrupted function from being changed.

So if you jump from one function and directely continue into the next function, the first function won't get focus. And if the function dont have focus it wont be able to cleanup - unless of cource, clean-up functions are running in its own threads.

>>I experienced situations where Delphi dereferenced interfaces instances earlier than at the end of the routine (e.g. inside of a big loop). So *no*, you can not rely on that dereferencing is only done at the end of a function.
<<
So have i. But i dont know whether this is an error in Delphi or not. Sometimes it helps to remove code-optimization ($O-) in the function.

Regards
Peter
0
 
LVL 20

Expert Comment

by:Madshi
ID: 8166204
I don't think it's a Delphi error, since the Delphi docs nowhere say that those invisible references only get freed at the end of a function. Remember, we're not talking about local variables, but about invisible references. Delphi is free to dereference them whenever it finds approriate.

Just imagine you had an endless loop, maybe a message loop, and Delphi would only dereference things on function exit, that would be terrible! The memory usage would steadily increase in this case and things would never be freed!
0
 
LVL 2

Expert Comment

by:PeterLarsen
ID: 8166288
>>
Just imagine you had an endless loop, maybe a message loop, and Delphi would only dereference things on function exit, that would be terrible! The memory usage would steadily increase in this case and things would never be freed!

You've got a point there !!
0
 
LVL 2

Expert Comment

by:PeterLarsen
ID: 8166406
Madshi,

It could still be an error in delphi, because the code-optimization (or memory optimization) shouldn't be freeing a var if it's in use later in the same function.

btw, i think we are agreed about what is the safest way, we just dont have it on paper :-)
0
 
LVL 20

Expert Comment

by:Madshi
ID: 8166603
>> the code-optimization shouldn't be freeing a var

But we're not talking about local variables here, but about invisible references.
0
 
LVL 2

Author Comment

by:classmate
ID: 8167478
Out of plain couriousity, i wrote this test

type
  TForm16 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    procedure Button1Click(Sender: TObject);

  private
    { Private declarations }
    FFirstTick : Integer;
    procedure Nop (Sender : TObject);
  public
    { Public declarations }
  end;

var
  Form16: TForm16;

implementation

{$R *.dfm}

procedure TForm16.Button1Click(Sender: TObject);
var
  l : TList;
  r,T, oldT : Integer;
  UnIdleMaker : TTimer;
begin
  l := TList.Create;
  UnIdleMaker := TTimer.Create(nil);
  UnIdleMaker.Interval := 126;
  UnIdleMaker.OnTimer := Nop; // need event handler to recieve timer messages

  l.Capacity := 30;
  T:=GetTickCount;
  oldT := T - 500;
  FFirstTick := T;

  l.Add (PChar(IntToStr(T)));
  l.Add (PChar(IntToStr(T)));
  l.Add (PChar(IntToStr(T)));
  l.Add (PChar(IntToStr(T)));
  l.Add (PChar(IntToStr(T)));
  l.Add (PChar(IntToStr(T)));
  l.Add (PChar(IntToStr(T)));
  l.Add (PChar(IntToStr(T)));
  l.Add (PChar(IntToStr(T)));
  l.Add (PChar(IntToStr(T)));


  while (t - FFirstTick < 10000) and not Application.terminated do begin
    if (T - oldT >= 500) then begin
      l.Add (PChar(IntToStr(t)));
      OldT := T;
      Button1.Caption := TimeToStr (Time);
    end;

    try
      Application.HandleMessage;
    except
      Application.HandleException(Application);
    end;

    T:= GetTickCount;
  end;

  if not Application.Terminated then
    for r := 0 to l.Count-1 do
      Memo1.Lines.Add (PChar(l[r]));

  UnIdleMaker.Free;
  l.free;
end;


procedure TForm16.Nop(Sender: TObject);
begin
end;


Though i won't trust it in any important case, it seems very much like strings fetched within the message loop are de-referenced during the loop, while those fetched outside are not dereferenced until the procedure's end; statement

:-)
classmate.
0
 
LVL 2

Author Comment

by:classmate
ID: 8167544
(oops that FFirstTick should by the way have been a local variable)
0
 
LVL 2

Expert Comment

by:PeterLarsen
ID: 8168818
>>But we're not talking about local variables here, but about invisible references.

You must have a local var if you are doing like this :

procedure DoSomething;
var
 PChar1, PChar2, PChar3 : PChar;
begin
 PChar1 := PChar (GetSomeString1);
 PChar2 := PChar (GetSomeString2);
 PChar3 := PChar (GetSomeString3);
 DoSomethingMore (PChar1, PChar2, PChar3);
end;
0
 
LVL 2

Author Comment

by:classmate
ID: 8172954
>>You must have a local var if you are doing like this

I can answer that:
The invisible references exist in addition to the local PChar vars.
0
 
LVL 20

Expert Comment

by:Madshi
ID: 8174650
Yep, classmate got it.   :-)
0
 
LVL 2

Expert Comment

by:PeterLarsen
ID: 8176379
arrghh, please !!!
The local var is (in this question) a pointer to the invisible var.

This is taken from the question (in case you've forgot it):

procedure DoSomething;
var
 PChar1, PChar2, PChar3 : PChar;
begin
 PChar1 := PChar (GetSomeString1);
 PChar2 := PChar (GetSomeString2);
 PChar3 := PChar (GetSomeString3);
 DoSomethingMore (PChar1, PChar2, PChar3);
end;

If the local var (PChar1 through 3) become nil, you don't know whether the invisible var still exist or not - you just have a local var which is no longer assigned any valid value/addresses.
If the local var get nil before "DoSomethingMore (PChar1, PChar2, PChar3);" it must be due to an error in Delphi.

>>Just imagine you had an endless loop, maybe a message loop, and Delphi would only dereference things on function exit, that would be terrible! The memory usage would steadily increase in this case and things would never be freed!

If you did this, you will probably eventually just get a 'Out of memory' message - but only if you actually use Result later on in the function. This is what code-optimization is used for.

So we are talking about local variables - actually, we are talking about the differences between local variables and 'hidden' variables and how Result is kept in the CALLING function.
0
 
LVL 20

Expert Comment

by:Madshi
ID: 8176617
Peter, you have to look behind what is obvious:

function GetSomeString1 : string;

What does this function return? It returns a string instance with (probably) the reference count 1. Now if we call it like this:

begin
  GetSomeString1;
end;

What happens? GetSomeString1 *DOES* return the string, but noone uses it. So we have an invisible string instance with the reference count 1. Now Delphi has to free that string somewhen. When does it do that?

begin
  strVar := GetSomeString1;
end;

Things are a bit different here. GetSomeString1 returns a string instance with reference count 1. The string is assigned to a string variable, so the reference count increases to 2 (!!). The invisible string reference returned by GetSomeString1 is still flying around. Delphi will release it somewhen, but we don't know exactly when. We do know that the strVar reference will be released exactly when strVar goes out of scope, which is at the end of the function. But the invisible reference might be freed whenever Delphi finds appropriate.

begin
  pcharVar := pchar(GetSomeString1);
end;

Now this is a tricky one. Basically it's like the first example: GetSomeString1 returns an invisible string instance. The string instance is *NOT* assigned to "pcharVar", that's not possible, since pcharVar is just a pchar variable. It can't hold a *string* instance. That means after that assignment pcharVar points to our invisible string reference. And pcharVar will continue to point at the invisible string reference until the calling function exits. However, the invisible string reference will be freed automatically by Delphi, we just don't know when. It might happen that Delphi frees it earlier than you like, e.g. directly after the "pcharVar := pchar(GetSomeString1)" assignment. After that moment pcharVar still continues to hold the same pointer ordinal value, but the string reference is already freed, so the pointer gets invalid in this moment.

I hope you see the problem now.
0
 
LVL 2

Expert Comment

by:PeterLarsen
ID: 8176850
There is nothing new here - of cource i know the differences between a string-var and a pointer-var.

begin
 pcharVar := pchar(GetSomeString1);
end;
I agree with you - this is a tricky one. But its not interesting.
I dont know with you, but i would never do this.
This bring me back to what i said earlier (a few 1000 lines ago) - you must have a OWNER if you want to control the data.
In this sample (pcharVar := pchar(GetSomeString1);) you are not the owner and therefor


I also know that call like this never will takes place - except if you remove the code-optimization.
begin
 GetSomeString1;
end;


0
 
LVL 2

Expert Comment

by:PeterLarsen
ID: 8176892
hmm, dont know what habbened - my comment was submitted automatically before it was ready ....
will try again !!
0
 
LVL 20

Expert Comment

by:Madshi
ID: 8176937
>> I also know that call like this never will takes place - except if you remove the code-optimization.

It will take place, with or without code optimization.
0
 
LVL 2

Expert Comment

by:PeterLarsen
ID: 8177007
>>begin
pcharVar := pchar(GetSomeString1);
end;
I agree with you - this is a tricky one. But its not interesting.
I dont know with you, but i would never do this.
This bring me back to what i said earlier (a few 1000 lines ago) - you must have a OWNER if you want to control the data.
In this sample (pcharVar := pchar(GetSomeString1);) you are not the owner and therefor << therefore you are not able to control when the memory should be released.

And about the lines :
>>I also know that call like this never will takes place - except if you remove the code-optimization.
begin
GetSomeString1;
end;<<
You wrote the lines (in your comment) because you want to show me something - which is fine.
But the lines was about to be removed because i have no need of telling you that i already know how this is working. Unfortunately my comment was submittet before i've got it finished.
0
 
LVL 2

Expert Comment

by:PeterLarsen
ID: 8177015
No it wont !!
0
 
LVL 20

Expert Comment

by:Madshi
ID: 8177085
Trust me.

If you don't, try it.
0
 
LVL 2

Expert Comment

by:PeterLarsen
ID: 8177162
Try read the entire comment before starts answering it !!
0

Featured Post

Want to be a Web Developer? Get Certified Today!

Enroll in the Certified Web Development Professional course package to learn HTML, Javascript, and PHP. Build a solid foundation to work toward your dream job!

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

A lot of questions regard threads in Delphi.   One of the more specific questions is how to show progress of the thread.   Updating a progressbar from inside a thread is a mistake. A solution to this would be to send a synchronized message to the…
Have you ever had your Delphi form/application just hanging while waiting for data to load? This is the article to read if you want to learn some things about adding threads for data loading in the background. First, I'll setup a general applica…
Do you want to know how to make a graph with Microsoft Access? First, create a query with the data for the chart. Then make a blank form and add a chart control. This video also shows how to change what data is displayed on the graph as well as form…
How to fix incompatible JVM issue while installing Eclipse While installing Eclipse in windows, got one error like above and unable to proceed with the installation. This video describes how to successfully install Eclipse. How to solve incompa…
Suggested Courses
Course of the Month14 days, 20 hours left to enroll

771 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question