Using null-terminated strings
Object Pascal supports a class of character strings called null-terminated strings. With Object Pascal’s extended syntax and the SysUtils unit, your Windows programs can use null-terminated strings by simply referring to the SysUtils unit with the uses clause in your program.
What is a null-terminated string?
The compiler stores a traditional Object Pascal string type as a length byte followed by a sequence of characters. The maximum length of a Pascal string is 255 characters, and a pascal string occupies from 1 to 256 bytes of memory.
A null-terminated string has no length byte; instead, it consists of a sequence of non- null characters followed by a NULL (#0) character. There is no inherent restriction on the length of a null-terminated string.
Using null-terminated strings
Null-terminated strings are stored as arrays of characters with a zero-based integer index type; that is, an array of the form
array[0..X] of Char
where X is a positive nonzero integer. These arrays are called zero-based character arrays. Here are some examples of declarations of zero-based character arrays that can be used to store null-terminated strings:
type
TIdentifier = array[0..15] of Char; TFileName = array[0..79] of Char; TMemoText = array[0..1023] of Char;
The biggest difference between using Pascal strings and null-terminated strings is the extensive use of pointers in the manipulation of null-terminated strings. Object Pascal performs operations on these pointers with a set of extended syntax rules.
Character pointers and string literals
When extended syntax is enabled, a string literal is assignment-compatible with the PChar type. This means that a string literal can be assigned to a variable of type PChar. For example,
var
P: PChar; ƒ
begin
P := 'Hello world...';
end;
The effect of such an assignment is that the pointer points to an area of memory that contains a null-terminated copy of the string literal. This example accomplishes the same thing as the previous example:
const
TempString: array[0..14] of Char = 'Hello world...'#0;
var
P: PChar; ƒ
begin
P := @TempString;
end;
You can use string literals as actual parameters in procedure and function calls when the corresponding formal parameter is of type PChar. For example, given a procedure with the declaration
procedure PrintStr(Str: PChar);
the following procedure calls are valid:
PrintStr('This is a test'); PrintStr(#10#13);
Just as it does with an assignment, the compiler generates a null-terminated copy of the string literal. The compiler passes a pointer to that memory area in the Str parameter of the PrintStr procedure.
Finally, you can initialize a typed constant of type PChar with a string constant. You can do this with structured types as well, such as arrays of PChar and records and objects with PChar fields.
const
Message: PChar = 'Program terminated'; Prompt: PChar = 'Enter values: '; Digits: array[0..9] of PChar = (
'Zero', 'One', 'Two', 'Three', 'Four',
'Five', 'Six', 'Seven', 'Eight', 'Nine');
A string constant expression is always evaluated as a Pascal-style string even if it initializes a typed constant of type PChar; therefore, a string constant expression is always limited to 255 characters in length.
Character pointers and character arrays
When the extended syntax is enabled, a zero-based character array is compatible with the PChar type. This means that whenever a PChar is expected, you can use a zero- based character array instead. When you use a character array in place of a PChar value, the compiler converts the character array to a pointer constant whose value corresponds to the address of the first element of the array. For example,
var
A: array[0..63] of Char; P: PChar;
ƒ
begin
P := A;
PrintStr(A);
PrintStr(P);
end;
Because of this assignment statement, P now points to the first element of A, so
PrintStr is called twice with the same value.
You can initialize a typed constant of a zero-based character array type with a string literal that is shorter than the declared length of the array. The remaining characters are set to NULL (#0) and the array effectively contains a null-terminated string.
type
TFileName = array[0..79] of Char;
const
FileNameBuf: TFileName = 'TEST.PAS'; FileNamePtr: PChar = FileNameBuf;
Character pointer indexing
Just as a zero-based character array is compatible with a character pointer, so can a character pointer be indexed as if it were a zero-based character array:
var
A: array[0..63] of Char; P: PChar;
Ch: Char; ƒ
begin
P := A;
Ch := A[5];
Ch := P[5];
end;
Both of the last two statements assign Ch the value contained in the sixth character element of A.
When you index a character pointer, the index specifies an unsigned offset to add to the pointer before it is dereferenced. Therefore, P[0] is equivalent to P^ and specifies the character pointed to by P. P[1] specifies the character right after the one pointed to by P, P[2] specifies the next character, and so on. For purposes of indexing, a PChar behaves as if it were declared as this:
type
TCharArray = array[0..65535] of Char; PChar = ^TCharArray;
The compiler performs no range checks when indexing a character pointer because it has no type information available to determine the maximum length of the null- terminated string pointed to by the character pointer. Your program must perform any such range checking.
The StrUpper function shown here illustrates the use of character pointer indexing to convert a null-terminated string to uppercase.
function StrUpper(Str: PChar): PChar;
var
I: Word;
begin
I := 0;
while Str[I] <> #0 do begin
Str[I] := UpCase(Str[I]); Inc(I);
end;
StrUpper := Str;
end;
Notice that StrUpper is a function, not a procedure, and that it always returns the value that it was passed as a parameter. Because the extended syntax allows the result of a function call to be ignored, StrUpper can be treated as if it were a procedure:
StrUpper(A);
PrintStr(A);
However, as StrUpper always returns the value it was passed, the preceding statements can be combined into one:
PrintStr(StrUpper(A));
Nesting calls to null-terminated string-handling functions can be very convenient when you want to indicate a certain interrelationship between a set of sequential string manipulations.
Note See page 46 for information about PChar operations.
Null-terminated strings and standard procedures
Object Pascal’s extended syntax allows the Read, Readln, Str, and Val standard procedures to be applied to zero-based character arrays, and allows the Write, Writeln, Val, Assign, and Rename standard procedures to be applied to both zero- based character arrays and character pointers.
C h a p t e r