I want to see the meta data for strings. I defined a record type and a utility class for this:
type
PStringMetaData = ^TStringMetaData;
TStringMetaData = packed record
codePage : word; // -12
charSize : word; // -10
refCount : integer; //-8
length : integer; //-4
chars : array [0..0] of char; // 0
end;
TStringUtility = class
public
class function getMetaData( const str_ : string ) : PStringMetaData;
end;
class function TStringUtility.getMetaData( const str_ : string ) : PStringMetaData;
begin
Result := PStringMetaData( pchar( str_ ) - 12 );
end;
But the TForm1.Button1Click
does not work as I expected.
procedure TForm1.Button1Click(Sender: TObject);
var
str, str2: string;
psmd : PStringMetaData;
begin
str := 'Hello, World!';
psmd := TStringUtility.getMetaData( str );
showmessage('Reference count: ' + StringRefCount( str ).ToString );
str2 := str;
showmessage('Reference count: ' + StringRefCount( str ).ToString );
end;
The StringRefCount function gives back -1. What is the reason?
The reason is how strings are handled internally.
The string 'Hello, World!'
is a string literal that the compiler puts into the binary. It is marked with a refcount of -1 to indicate that it is a string literal.
When assigning a string to a local string variable, the compiler inserts a call to System._UStrLAsg
, resulting in the local variable containing the reference to the string literal. This is an optimization because a local variable cannot escape the current routine and thus it is safe to point to the string literal.
Only when assigning to a field, a global variable, or a function result, the compiler uses a call to System._UStrAsg
which handles the special case of refcount -1 and then allocates a new string and copies the content of the string literal into it.
Furthermore, your TStringMetaData
declaration is wrong - the fields are in incorrect order and the PChar
field is simply wrong. See the documentation for details.