Ross Levis wrote:
>> Nope. Delphi's AnsiString value actually points to the char field of 
>> the struct, not the length field.
> 
> I didn't think this was correct because when reading or writing a string 
> to a file using blockread or blockwrite, I need to use MyString[1] as 
> the reference.
> 
> eg. BlockWrite(File1, MyString[1], Length(MyString));
> 
> If I specify just MyString, it accesses memory that does not contain the 
> string, most likely the length integer which is MyString[0].
> 
> Perhaps you can explain to me a bit more on how this works.

Here's the layout of an AnsiString:

Reference count (4 bytes)
Length (4 bytes)
First character (1 byte)
Second character (1 byte)
Third character (1 byte)
...
Nth character (1 byte)
Null character (1 byte)

There are no pointers within the AnsiString structure itself. For a 
string of N characters, that's N+9 bytes. A variable of type AnsiString 
is always four bytes. It stores a pointer to the main structure, but not 
a pointer to the first byte of that structure. Rather, it points to the 
*ninth* byte -- the byte occupied by the first character of string data.

When your program reads the length of a string, it subtracts 4 from the 
address stored in the AnsiString variable. That new pointer points to 
the length field. Dereferencing that address yields the length value. 
One way to implement the Length function would be like this:

function Length(const s: AnsiString): LongInt;
var
   L: PLongInt;
begin
   L := Pointer(s);
   if Assigned(L) then begin
     Dec(L);
     Result := L^;
   end else
     Result := 0;
end;

Anyway, back to BlockWrite. BlockWrite accepts an untyped const 
parameter. If you just call it like this,

BlockWrite(File1, MyString, Length(MyString));

It will write some number of bytes to the file. The first four of those 
bytes will be the four bytes occupied by the MyString variable. In other 
words, you'd be storing the address of the first character of the 
string. It does not touch any of the string structure I described above. 
Untyped parameters do not dereference the things you give to them.

If the length of the string is greater than four, then it's uncertain 
just what else you're write into that file. It will be some of the 
memory adjacent to the MyString variable on the stack. Perhaps some 
variables, perhaps a return address, and whatever else the compiler 
decided to store there.

The right way to call BlockWrite (and anything that uses untyped 
parameters) if as you showed:

BlockWrite(File1, MyString[1], Length(MyString));

With that call, you give BlockWrite the first character of the string, 
and it continues reading the adjacent bytes to get the rest of the 
string -- however many bytes are indicated by the call to Length.

Everyone will be better off if they just forget there was ever anything 
of use at index 0 of a string. For AnsiString and WideString, reading 
that value gives a range-check error. For _all_ strings, the Length 
function gives the string's length; you can call it without regard for 
what kind of string you're really working with.

-- 
Rob
_______________________________________________
Delphi mailing list -> [email protected]
http://www.elists.org/mailman/listinfo/delphi

Reply via email to