3.2.7 PChar – Null terminated strings

Free Pascal supports the Delphi implementation of the PChar type. PChar is defined as a pointer to a Char type, but allows additional operations. The PChar type can be understood best as the Pascal equivalent of a C-style null-terminated string, i. e. a variable of type PChar is a pointer that points to an array of type Char, which is ended by a null-character (#0). Free Pascal supports initializing of PChar typed constants, or a direct assignment. For example, the following pieces of code are equivalent:

program one;
var P : PChar;
begin
  P := 'This is a null-terminated string.';
  WriteLn (P);
end.

Results in the same as

program two;
const P : PChar = 'This is a null-terminated string.';
begin
  WriteLn (P);
end.

These examples also show that it is possible to write the contents of the string to a file of type Text. Since it is equivalent to a pointer to a type Char variable, it is also possible to do the following:

Program three;
Var S : String[34];
    P : PChar;
begin
  S := 'This is a null-terminated string.'#0;
  P := @S[1];
  WriteLn (P);
end.

This will have the same result as the previous two examples.

Note that characters in String start with 1, not 0. This is important to consider when assigning a pointer:

P := @S;    // error, will point to the "length byte"
P := @S[0]; // error as above
P := @S[1]; // correct

As already shown, for a ShortString (unlike AnsiString), a terminating null byte must be explicitly added if you want to use it as a reference for a PChar. This should also be done when assigning empty strings:

var
  S: ShortString;
  P: PChar;
begin
  S := 'abcd';
  S := '12';
  P := @S[1];
  WriteLn(P);   // print "12cd" and possibly other garbage
  S := '12'+#0;
  WriteLn(P);   // print "12"
  S := #0;
  WriteLn(P);   // print ""
end.

Remark
This example also demonstrates that a PChar is not a separate string, but a reference to an existing one (or even part of one). Therefore, changing the contents of S also affects P.

The maximum length of a short string must accommodate the entire text, including the terminating null byte.

Remark
There are many ways to write the null character:

S:='abcd'#0;       // character constant
S:='abcd'+#0;      // character constant (alternative)
S:='abcd'+chr(0);  // function Chr
S:='abcd'+char(0); // type casting

Due to the nature of PChar strings, many standard string operations (e.g. concatenation) are unavailable. To work with them correctly, functions from the strings unit must be used as in the standard C library.

However, some still work. For example, to obtain string length (taking care of the null character), the standard Length function can be used if the PChar type is explicitly specified (or cast):

var
  S: String = 'ab'+#0+'cd';
  P: PChar;
begin
  P:=@S[1];
  WriteLn(Length(S));            // 5
  WriteLn(Length(P));            // 2
  WriteLn(Length(PChar(@S[1]))); // 2

Also note some peculiarities of pointer arithmetic. The operators + and - can be used to do operations on PChar pointers. In table (3.6), P and Q are of type PChar, and I is of type Longint.


Table 3.6: PChar pointer arithmetic

Operation Result


P + I Adds I to the address pointed to by P.
I + P Adds I to the address pointed to by P.
P - I Subtracts I from the address pointed to by P.
P - Q Returns, as an integer, the distance between two addresses
(or the number of characters between P and Q)



PChar doesn’t necessarily point to the beginning of the string, but then you must ensure it points to the correct data. In this case, it will act as a string ”truncated” from the beginning:

var
  S: String = 'abcd'#0;
  P: PChar;
begin
  P:=@S[3];
  WriteLn(P); // cd

Note that ”extra” null characters within the string can even split it into ”multiple” PChar strings:

var
  S: String = 'ab'+#0+'cd'+#0;
  P: PChar;
begin
  P:=@S[1];
  WriteLn(P); // ab
  P:=P+3;
  WriteLn(P); // cd

In general, it’s recommended to be careful with them, especially when writing.