4

According to the information in the internet I found out that two following variables point to the same place in memory.

Could anyone propose a code example to demonstrate that in fact it is true (e.g. by changing one of the letter in the 1st variable and see that this change is visible from the second variable)?

procedure TForm1.Button1Click(Sender: TObject);
var
  a, b: String;
begin
  a := 'Test';
  b := a;

  showmessage (a);
  showmessage (b);
end;
Rob Kennedy
  • 161,384
  • 21
  • 275
  • 467
Paul
  • 219
  • 3
  • 10
  • It's true for dynamic arrays too. What's different is that strings implement copy on write but strings don't. – David Heffernan Mar 16 '11 at 15:19
  • @Paul, your title sounds like your question is related to reference counting, but your question test is about variables pointing to the same address... I think you shall find a better title and edit the question. – jachguate Mar 16 '11 at 15:23
  • I think that the The title is appropriate as in my code example assigning variable causes reference incrementing. – Paul Mar 16 '11 at 15:27
  • `a := 'Test'; b := 'Test';` results in `a` and `b` having different storage addresses. – David Heffernan Mar 16 '11 at 15:38
  • 6
    Really, @David? It shouldn't. Those should both refer to the same literal, and should both have a reference count of -1. – Rob Kennedy Mar 16 '11 at 15:41
  • 1
    @David, @Rob: I just tested it. Rob is right and David is wrong (sorry!). – Andreas Rejbrand Mar 16 '11 at 15:43
  • @Jachguate, if you don't like a title, you can edit it to make it better. Its *encouraged*, even. – Rob Kennedy Mar 16 '11 at 16:08
  • OK fair enough, I didn't think it through. How about this one though, `a := 'Test'; b := 'Te'+'st';` These will have different addresses. Or my previous example but the two assignments written in different procedures. – David Heffernan Mar 16 '11 at 16:10
  • @Rob I do it sometimes.. this one I just didn't get a suitable title in two or three time-slices, and I think it should be encouraged to educate people to find good titles by themselves also. :) – jachguate Mar 16 '11 at 16:55
  • @David: Same constant assigned to variables in different procedures would point to the same address, unless in D2009+ the variables are of different string types as Delphi generates constant values with different encodings based on the use of the constant. See http://stackoverflow.com/questions/4292611/is-there-some-advantage-in-use-resourcestring-instead-of-a-const-string/4293415#4293415 for how Delphi stores resource strings and string consts. – Marjan Venema Mar 16 '11 at 17:13
  • @marjan and literals? Compiler won't hunt down duplicates of those surely? – David Heffernan Mar 16 '11 at 18:00
  • @David: I think it does. I will check to make sure, but that will have to wait until after my running training... :-) – Marjan Venema Mar 16 '11 at 18:03
  • @David: Compiler de-dupes string literals within the same method, but you are right it doesn't de-dupe across methods. – Marjan Venema Mar 16 '11 at 22:19
  • @marjan across unit de-dupe would require second pass – David Heffernan Mar 16 '11 at 22:21
  • @David, @Rob: doing a := TEST_CONST; and b:= TEST_CONST; does pull the the value from the same address, but results in two copies in memory with different storage addresses. Behind the scenes, Delphi is doing a UniqueStringU (not the exact name) call, whereas it did not use to do that pre D2009. Checked it in both D2009/D2010. Don't know whether this is still the case in XE (don't have it available). After the assignment, reference counting and copy-on-write do kick in. Should be the same for string literals. @Andreas: With which version of Delphi did you check @David's statement? – Marjan Venema Mar 17 '11 at 07:55
  • @Marjan @Rob @Andreas Writeable typed const? Even if you don't have it on, the compiler maybe handles consts under the assumption that you might. – David Heffernan Mar 17 '11 at 08:10
  • @David, @Rob, @Andreas: Assignalble typed const was off when I checked earlier. Turning it on didn't make a difference... – Marjan Venema Mar 17 '11 at 08:18
  • @marjan was your constant typed? – David Heffernan Mar 17 '11 at 08:30
  • @David: It was untyped. But making it typed (`TEST_CONST: string = 'Test'`) makes no difference. Tried all 4 combinations of typed/untyped and assignable/non-assignable. UniqueStringU keeps getting called... – Marjan Venema Mar 17 '11 at 08:45
  • @Marjan I can't reproduce your reported findings! – David Heffernan Mar 17 '11 at 08:56
  • @David: Let's get off this comment trail. I will send you my test project by mail. – Marjan Venema Mar 17 '11 at 09:04

5 Answers5

8
procedure TForm4.FormCreate(Sender: TObject);
var
  a, b: string;
begin
  a := 'Test';
  b := a;
  ShowMessage(BoolToStr(pointer(a) = pointer(b), true));
end;

The result is True, so yes, a and b point to the same data.

Notice, however, that

procedure TForm4.FormCreate(Sender: TObject);
var
  a, b: string;
begin
  a := 'Test';
  b := a;
  b := 'Test2';
  ShowMessage(BoolToStr(pointer(a) = pointer(b), true));
end;

displays False, as it should be.

In addition, notice that

procedure TForm4.FormCreate(Sender: TObject);
var
  a, b: string;
begin
  a := 'Test';
  b := a;
  ShowMessage(BoolToStr(@a = @b, true));
end;

also displays False, because a and b are different string (pointer) variables, so at some place in memory (@a) is the address of the data of a, and somewhere else (@b) is the address of the data of b. The first example shows that these two places in memory contain the same address, that is, that a and b contain the same data.

Andreas Rejbrand
  • 105,602
  • 8
  • 282
  • 384
  • Well, thanks, but can you propose an example to change the content one of these two variable? – Paul Mar 16 '11 at 15:21
3

Normally Delphi use 'copy-on-write' semantics for strings, so you need a kind of hacking to do the trick, for example:

procedure TForm13.Button1Click(Sender: TObject);
const
  Test: string = '12345';

var
  S1, S2: string;
  P: PChar;

begin
  SetString(S1, PChar(Test), 5);
// we need to copy '12345' string from readonly memory to heap
  S2:= S1;
// Now both S1 and S2 points to the same memory

  P:= Pointer(S1);
  P^:= 'A';
  ShowMessage(S2);  // 'A2345'
end;
kludg
  • 27,213
  • 5
  • 67
  • 118
  • If you do this for real in some production code, and you work with me, I leave a rotten Herring carcase on your desk. – Warren P Mar 16 '11 at 17:47
1
var
  a, b: string;
begin
  a := 'Test';
  a := a + '!'; // added after Rob's comment below, 
                // makes sure a points to an allocation on the heap
  b := a;
  PChar(b)[3] := 'T';
  ShowMessage(a); //--> TesT!
end;
Thorsten Engler
  • 2,333
  • 12
  • 13
  • +1 This was already answered, but your answer shows a nice way to see it really points to the same data, not just by using pointers, but the data. :) – jachguate Mar 16 '11 at 16:01
  • If this really behaves the way you say it does, then it's a remarkable demonstration. It either shows that the compiler knows that the string in `a` will be modified via `b` and therefore assigns a writable string instead of a literal (which is a feat of static analysis I don't associate with Delphi), or it shows that literals aren't stored in read-only memory with the rest of the program's data. – Rob Kennedy Mar 16 '11 at 16:06
  • @Rob Kennedy, you are right, the above might crash. I'll fix it. – Thorsten Engler Mar 16 '11 at 16:08
0

Your question is not very clear for me. If you do :

begin 
  a := 'Test';
  b := a;
  a := a+'HH';
  showmessage (a);
  showmessage (b); 
end;

I think you will see it...

philnext
  • 3,242
  • 5
  • 39
  • 62
0

OK may be more clear with this code

procedure TForm1.FormCreate(Sender: TObject);

var
  a, b , s : string;
  p : pointer;
begin
  a := 'Test';
  b := a;
// we see the 2 diff. var pointing on the same adress
  s := Format('%p -> %p / %p -> %p', [@pointer(a),pointer(a),@pointer(b),pointer(b)] );
  ShowMessage( 'first : '+s);
// we see the 2 diff. var pointing on different adresses
  a := 'Test2';
  s := Format('%p -> %p / %p -> %p', [@pointer(a),pointer(a),@pointer(b),pointer(b)] );
  ShowMessage( 'second : '+s);
end;
philnext
  • 3,242
  • 5
  • 39
  • 62