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