I have the method (Delphi 2009):
procedure TAnsiStringType.SetData(const Value: TBuffer; IsNull: boolean = False);
begin
if not IsNull then
FValue:= PAnsiString(Value)^;
inherited;
end;
This is an abstract method on the base class, where "Value: Pointer" expects the pointer of the corresponding data, as:
String = PString
AnsiString = PAnsiString
Integer = PInteger
Boolean = PBoolean
So I try to pass the value like this:
var
S: AnsiString;
begin
S:= 'New AnsiString Buffer';
SetBuffer(PAnsiString(S));
end;
But a cast from AnsiString to PAnsiString does NOT work, I can see why, but I want to know what the result of the casting is. So I wrote a simple test:
var
Buffer: AnsiString;
P1: Pointer;
P2: Pointer;
P3: Pointer;
P4: Pointer;
begin
P1:= PAnsiString(Buffer);
P2:= Addr(Buffer);
P3:= @Buffer;
P4:= Pointer(Buffer);
P5:= PChar(Buffer[1]);
WriteLn('P1: ' + IntToStr(Integer(P1)));
WriteLn('P2: ' + IntToStr(Integer(P2)));
WriteLn('P3: ' + IntToStr(Integer(P3)));
WriteLn('P4: ' + IntToStr(Integer(P4)));
WriteLn('P5: ' + IntToStr(Integer(P5)));
end;
The result is:
P1: 5006500
P2: 1242488
P3: 1242488
P4: 5006500
P5: 67
Where:
- P2 and P3, is the address of Buffer: AnsiString
- P5 is the Char Ord value of Buffer[1] char, in this case "67 = C"
- How about P1 and P4?
What is the meaning of P1 and P4?
An AnsiString
is implemented as a pointer. An AnsiString
variable holds nothing but an address. The address is that of the first character in the string, or nil
if the string is empty.
A PAnsiString
is a pointer to an AnsiString
variable. It's a pointer to a pointer to the first character of a string. When you say PAnsiString(Buffer)
, you're telling the compiler to treat the pointer in Buffer
as though it were a pointer to an AnsiString
instead of a pointer to character data. The address 5006500 is the location of the first character of the string, C
.
You have a record in memory that represents the string:
+-----------+ | $ffffffff | -1 reference count (4 bytes) +-----------+ Buffer: | $00000001 | length (4 bytes) +---------+ +-----------+ | 5006500 | --> | 'C' | first character (1 byte) +---------+ +-----------+ | #0 | null terminator (1 byte) +-----------+
Buffer
holds the address of the byte with C
in it. You type-cast that to have type PAnsiString
instead of AnsiString
. You told the compiler that you had this layout:
+-----------+ | ... | +-----------+ Buffer: | ... | +---------+ +-----------+ +-----------+ | 5006500 | --> | $00000043 | --> | garbage | first character +---------+ +-----------+ +-----------+ | ... | +-----------+
When I'm reasoning about pointers, I draw diagrams just like this. If you don't keep some paper next to you on your desk, you're doing yourself a disservice.
Nice puzzle, but I have the solution:
I have added comment in the code:
var
Buffer: AnsiString;
P1: Pointer;
P2: Pointer;
P3: Pointer;
P4: Pointer;
P5: Pointer;
begin
P1:= PAnsiString(Buffer);
(* A cast from AnsiString to PAnsiString has no real meaning
because both are a pointer to a block of characters ()
P2:= Addr(Buffer);
P3:= @Buffer;
(* Both Addr and @ give the address of a variable. The variable Buffer is
a pointer so we get the address of the pointer, not the value of the
pointer. *)
P4:= Pointer(Buffer);
(* See the remark on P1. Due to the cast both give the same result. *)
P5:= PChar(Buffer[1]);
(* This looks like a pointer to the first element. But the cast changes
it into the character. *)
WriteLn('P1: ' + IntToStr(Integer(P1)));
WriteLn('P2: ' + IntToStr(Integer(P2)));
WriteLn('P3: ' + IntToStr(Integer(P3)));
WriteLn('P4: ' + IntToStr(Integer(P4)));
WriteLn('P5: ' + IntToStr(Integer(P5)));
end;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With