Converting String to PChar in Delphi

Long string to PChar conversions are not automatic. Some of the differences between strings and PChars can make conversions problematic:

  • Long strings are reference-counted, while PChars are not.
  • Assigning to a string copies the data, while a PChar is a pointer to memory.
  • Long strings are null-terminated and also contain the length of the string, while PChars are simply null-terminated.

Sometimes you need convert a long string to a null-terminated string, for example, if you are using a function that takes a PChar. If you must cast a string to a PChar, be aware that you are responsible for the lifetime of the resulting PChar.

Because long strings are reference counted, typecasting a string to a PChar increases the dependency on the string by one, without actually incrementing the reference count. When the reference count hits zero, the string will be destroyed, even though there is an extra dependency on it.

The cast PChar will also disappear, while the routine you passed it to may still be using it. For example:

procedure my_func(x: string); begin // do something with x some_proc(PChar(x)); // cast the string to a PChar // you now need to guarantee that the string remains // as long as the some_proc procedure needs to use it end;

A common error when working with PChars is to store a local variable in a data structure, or return it as a value. When your routine ends, the PChar disappears because it is a pointer to memory, and not a reference counted copy of the string. For example:

function title(n: Integer): PChar; var s: string; begin s := Format(‘title - %d’, [n]); Result := PChar(s); // DON’T DO THIS end;

This example returns a pointer to string data that is freed when the title function returns.

Consider the case where you have a local string variable that you need to initialize by calling a function that takes a PChar. One approach is to create a local array of char and pass it to the function, then assign that variable to the string:

// assume FillBuffer is a predefined function function FillBuffer(Buf:PChar;Count:Integer):Integer begin . . . end; // assume MAX_SIZE is a predefined constant var i: Integer; buf: array[0..MAX_SIZE] of char; S: string; begin i := FillBuffer(0, buf, SizeOf(buf));// treats buf as a PChar S := buf; //statements end;

This approach is useful if the size of the buffer is relatively small, since it is allocated on the stack. It is also safe, since the conversion between an array of char and a string is automatic. The Length of the string is automatically set to the right value after assigning buf to the string.

To eliminate the overhead of copying the buffer, you can cast the string to a PChar (if you are certain that the routine does not need the PChar to remain in memory). However, synchronizing the length of the string does not happen automatically, as it does when you assign an array of char to a string.

You should reset the string Length so that it reflects the actual width of the string. If you are using a function that returns the number of bytes copied, you can do this safely with one line of code:

var S: string; begin SetLength(S, MAX_SIZE;// when casting to a PChar, be sure the string is not empty SetLength(S, GetModuleFilename( 0, PChar(S), Length(S) ) ); // statements end;

The following compiler directives affect character and string types:

  • {$H+/-} - A compiler directive, $H, controls whether the reserved word string represents a short string or a long string. In the default state, {$H+}, string represents a long string. You can change it to a ShortString by using the {$H-} directive.
  • {$P+/-} - The $P directive is meaningful only for code compiled in the {$H-} state, and is provided for backwards compatibility. $P controls the meaning of variable parameters declared using the string keyword in the {$H-} state.

In the {$P-} state, variable parameters declared using the string keyword are normal variable parameters, but in the {$P+} state, they are open string parameters. Regardless of the setting of the $P directive, the OpenString identifier can always be used to declare open string parameters.

  • {$V+/-} - The $V directive controls type checking on short strings passed as variable parameters. In the {$V+} state, strict type checking is performed, requiring the formal and actual parameters to be of identical string types.

In the {$V-} (relaxed) state, any short string type variable is allowed as an actual parameter, even if the declared maximum length is not the same as that of the formal parameter. Be aware that this could lead to memory corruption.

  • {$X+/-} - The {$X+} compiler directive enables support for null-terminated strings by activating the special rules that apply to the built-in PChar type and zero-based character arrays. (These rules allow zero-based arrays and character pointers to be used with Write, Writeln, Val, Assign, and Rename from the System unit.)