Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-pascal / 2024-03

AnsiString address changed

9 messages · 5 people ·

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Hairy Pixels

Curious, why did the address of "s" change here? Shouldn't the AnsiString be incrementing a reference count and not actually changing the actual pointer or copying?

Correct me if I'm wrong, AnsiString is ref counted when passing in/out functions but copies on assignment so technically no two AnsiStrings can be shared in two locations, which makes sense because if they were resized they would invalidate each others memory anyways when ReAllocMem is called.

I guess if that's true then there must be only pointer to any AnsiString in any given scope otherwise they could resize and corrupt their memory.

Not sure if that makes sense, I'm struggling to understand myself. :)

============================================

  procedure PassString(s: AnsiString);
  begin
    writeln(hexstr(@s));
  end;
var
  s1: AnsiString;
begin
  s1 := '123';
  writeln(hexstr(@s1));
  PassString(s1);
  writeln(hexstr(@s1));
end.

Output:

000000010259AC00
000000016DBC2F80
000000010259AC00

Regards,
Ryan Joseph

Reply by email

Michael Van Canneyt
to Hairy Pixels
Hairy Pixels wrote 29 lines
Curious, why did the address of "s" change here? Shouldn't the AnsiString be incrementing a reference count and not actually changing the actual pointer or copying? Correct me if I'm wrong, AnsiString is ref counted when passing in/out functions but copies on assignment so technically no two AnsiStrings can be shared in two locations, which makes sense because if they were resized they would invalidate each others memory anyways when ReAllocMem is called. I guess if that's true then there must be only pointer to any AnsiString in any given scope otherwise they could resize and corrupt their memory. Not sure if that makes sense, I'm struggling to understand myself. :) ============================================ procedure PassString(s: AnsiString); begin writeln(hexstr(@s)); end; var s1: AnsiString; begin s1 := '123'; writeln(hexstr(@s1)); PassString(s1); writeln(hexstr(@s1)); end. Output: 000000010259AC00 000000016DBC2F80 000000010259AC00

An ansistring is a pointer to a memory block.

You are printing the address of S1 and the address of S, i.e. the address of the pointer itself, not the address of what S (or s1) points to. Obviously the address of the s is different of s1.

What you want to do is print hexstr(pointer(s))
if you do that, you'll have the same output 3 times.

Michael.

Reply by email

Hairy Pixels
to Michael Van Canneyt
Show quoted text 9 lines
On Mar 18, 2024, at 1:52 PM, Michael Van Canneyt via fpc-pascal wrote: An ansistring is a pointer to a memory block. You are printing the address of S1 and the address of S, i.e. the address of the pointer itself, not the address of what S (or s1) points to. Obviously the address of the s is different of s1. What you want to do is print hexstr(pointer(s)) if you do that, you'll have the same output 3 times.

Oh, it's a pointer to a pointer? I guess that explains how it can resize itself and not invalidate shared references, if those are even possible with AnsiString.

Regards,
Ryan Joseph

Reply by email

Hairy Pixels
to Hairy Pixels
On Mar 18, 2024, at 3:27 PM, Hairy Pixels wrote: Oh, it's a pointer to a pointer? I guess that explains how it can resize itself and not invalidate shared references, if those are even possible with AnsiString.

Wait, that's totally wrong. :) @s is the address of the local variable of course. I didn't think before I did that. I just wanted the address of the AnsiString.

That still doesn't explain how they resize though. There must be not be more than 1 reference to any AnsiString at a time.

Regards,
Ryan Joseph

Reply by email

Michael Van Canneyt
to Hairy Pixels: Wait, that's totally wrong. :) @s is the address of the local variable of course. I…
Hairy Pixels wrote 9 lines
> On Mar 18, 2024, at 3:27 PM, Hairy Pixels wrote: > > Oh, it's a pointer to a pointer? I guess that explains how it can resize itself and not invalidate shared references, if those are even possible with AnsiString. Wait, that's totally wrong. :) @s is the address of the local variable of course. I didn't think before I did that. I just wanted the address of the AnsiString. That still doesn't explain how they resize though. There must be not be more than 1 reference to any AnsiString at a time.

Of course there must be, that's the whole point of copy-on-write.

As soon as one reference is changed, a copy is made if the reference count is larget than 1, and this copy is changed.

Michael.

Reply by email

Hairy Pixels
to Michael Van Canneyt
Show quoted text 6 lines
On Mar 18, 2024, at 5:29 PM, Michael Van Canneyt via fpc-pascal wrote: Of course there must be, that's the whole point of copy-on-write. As soon as one reference is changed, a copy is made if the reference count is larget than 1, and this copy is changed.

Oh, it does copy on write. I see now that by looking at the pointer addresses.

So what happens if you do this? s2 is pointing to s1 but then you change the size of s1 and ReAllocMem gets called and invalidates the pointer to the original s1. How does s2 know this and not access corrupted memory now?

  s1 := '123';
  s2 := s1;
  s1 := 'xxxxxxxxxxxx';

Regards,
Ryan Joseph

Reply by email

Tomas Hajny
to Hairy Pixels
Hairy Pixels wrote 20 lines
> On Mar 18, 2024, at 5:29 PM, Michael Van Canneyt via fpc-pascal > wrote: > > Of course there must be, that's the whole point of copy-on-write. > > As soon as one reference is changed, a copy is made if the reference > count > is larget than 1, and this copy is changed. Oh, it does copy on write. I see now that by looking at the pointer addresses. So what happens if you do this? s2 is pointing to s1 but then you change the size of s1 and ReAllocMem gets called and invalidates the pointer to the original s1. How does s2 know this and not access corrupted memory now? s1 := '123'; s2 := s1; s1 := 'xxxxxxxxxxxx';

From user point of view, the result is the same as when s1 and s2 are declared as shortstring (i.e. s2 = '123' and s1 = 'xxxxxxxxxxxxx'). From technical point of view, the original location is used for s2 (with reference count 1), whereas s1 points to a new location (with reference count also 1 at that point in time).

Tomas

Reply by email

Sven Barth
to Hairy Pixels: Oh, it does copy on write. I see now that by looking at the pointer addresses. So what…

Hairy Pixels via fpc-pascal schrieb am
Mo., 18. März 2024, 13:30:

Show quoted text 22 lines
> On Mar 18, 2024, at 5:29 PM, Michael Van Canneyt via fpc-pascal < > wrote: > > Of course there must be, that's the whole point of copy-on-write. > > As soon as one reference is changed, a copy is made if the reference count > is larget than 1, and this copy is changed. Oh, it does copy on write. I see now that by looking at the pointer addresses. So what happens if you do this? s2 is pointing to s1 but then you change the size of s1 and ReAllocMem gets called and invalidates the pointer to the original s1. How does s2 know this and not access corrupted memory now? s1 := '123'; s2 := s1; s1 := 'xxxxxxxxxxxx';

Your description is inaccurate: s2 does not point to s1. Instead both point to the string data with a reference count of 2. If you now change one of the two (e.g. with SetLength) then a copy of the string data will be created and that copy will be manipulated.
The memory of the string data will only be reallocated if only one reference to that data exists.

Regards,
Sven

Reply by email