PChar

Top  Previous  Next

Pretranslated C# code > Pointers > PChar

`PChar` simulates Delphi `PChar` and, in the Unicode runtime, `PWideChar`.

 

public struct PChar : IPointer<char>

 

It is a UTF-16 character pointer built on `Pointer<char>`. Arithmetic and indexed access are measured in UTF-16 code units.

 

`PChar` is intended for translated code that uses functions such as `StrLen`, `StrCopy`, `StrPCopy`, `StrAlloc`, `StrDispose`, `FormatBuf`, or direct null-terminated character traversal.

 

Value semantics

 

`PChar` is a struct. Copying a `PChar` copies its current position while sharing the backing buffer:

 

PChar p = new PChar(buffer);

PChar q = p + 1;

 

`p` and `q` have independent positions but observe the same character data.

 

Null pointers

 

Use `default` for Delphi `nil`:

 

PChar p = default;

 

Test with:

 

if (p.IsNull())

{

}

 

Dereferencing a null `PChar` throws `NullReferenceException`.

 

Writable string copy

 

Constructing `PChar` from `string` creates a writable, null-terminated `char[]` copy:

 

PChar p = new PChar("Hello");

p[0] = 'h';

 

This does not modify the original C# string because `System.String` is immutable.

 

The copied content can be converted back explicitly:

 

string changed = p.ToString();

 

or, for legacy generated code:

 

p.Synchronize(ref text);

 

Synchronization is an explicit copy operation. It is not automatic.

 

Read-only non-copying string view

 

When only reading is required, use:

 

PChar p = PChar.FromStringView(text);

 

This creates a non-copying UTF-16 view of the C# string. The view includes a logical trailing null character. Any write through this pointer throws `InvalidOperationException`.

 

Character-array backing

 

char[] buffer =

{

    'H', 'e', 'l', 'l', 'o', '\0'

};

 

PChar p = new PChar(buffer);

 

By default, this is a non-copying view. Writes through `p` directly modify `buffer`.

 

The constructor does not append a terminator to an existing `char[]`. Code that treats the array as a null-terminated string must provide sufficient storage and a terminating `\0`.

 

Use `copy: true` when an independent array copy is required:

 

PChar copy = new PChar(buffer, 0, copy: true);

 

 

Allocated buffers

 

 

PChar managed = new PChar(characterCount);

PChar native = new PChar(characterCount, alloc: true);

 

The managed form uses a zero-initialized `char[]`. The native form allocates owned native storage of `characterCount * sizeof(char)` bytes.

 

Release owned native storage with:

 

DelphiMemory.FreeMem(ref native);

 

`Dispose()` or `SetNull()` only clears the current pointer value.

 

Dereferencing and indexed access

 

 

char c = p.Deref();

p.Assign('A');

 

char next = p[1];

p[1] = 'B';

 

 

These correspond to:

 

C := P^;

P^ := 'A';

C := P[1];

P[1] := 'B';

 

Pointer arithmetic

 

p++;

p--;

p = p + 4;

p = p - 2;

 

 

Each step advances by one UTF-16 code unit. Internally, one character step normally advances by two bytes.

 

Changing the dereferenced character

 

The compatibility helpers:

 

p.DerefAdd(value);

p.DerefSubstract(value);

 

support translated code such as:

 

Inc(P^, Value);

Dec(P^, Value);

 

Length and capacity

 

`Length` scans from the current position to the first null character, or to the end of the current view if no terminator is present.

 

`Capacity` reports the number of character slots available from the current position. It is not the same as the null-terminated string length.

 

Writing strings

 

p.Assign("Text");

 

writes the characters followed by `\0` into the existing backing.

 

The pointer never automatically resizes its backing. If the view does not contain enough writable character slots, an exception is thrown.

 

The same rule applies to `Insert`.

 

Conversion to managed values

 

string text = p.ToString();

char[] chars = p.ToCharArray();

string part = p.Substring(from, count);

 

`ToString()` and `ToCharArray()` start at the current pointer position and stop before the first null character. `ToCharArray()` does not include the terminator.

 

Writable C# string buffers

 

For a native-style operation that must modify a C# string, use `Utf16StringBuffer`:

 

using Utf16StringBuffer buffer =

    new Utf16StringBuffer(text, capacity);

 

PChar p = buffer.Pointer;

 

// Pointer operations modify the buffer.

 

text = buffer.Commit();

 

 

`Commit()` creates the resulting immutable C# string from the buffer. This is the preferred replacement for old implicit `PChar` synchronization patterns.

 

Native API interoperability

 

`ToIntPtr()` returns the current address. Managed backing is pinned.

 

For scoped calls, prefer:

 

using PointerPin pin = p.Pin();

NativeFunction(pin.Address);

 

There is no separate native mirror that must be copied back with `FromIntPtr()`. Managed and native views already refer to their actual shared backing. `PChar.FromIntPtr()` is therefore only a compatibility no-op in the current runtime.

 

Memory cleanup

 

`PChar` implements `IDisposable` because it implements `IPointer<char>`, but `Dispose()` only clears that pointer value. It does not free a shared allocation.

 

For memory allocated by `StrAlloc`, `GetMem`, or the native `PChar` constructor, use the appropriate runtime free operation, normally:

 

DelphiMemory.FreeMem(ref p);

 

Typical translated code

 

Delphi:

 

P := PChar(S);

while P^ <> #0 do

  Inc(P);

 

C# with a read-only view:

 

PChar p = PChar.FromStringView(s);

 

while (p.Deref() != '\0')

{

    p++;

}

 

Delphi:

 

P[0] := 'A';

 

C#:

 

p[0] = 'A';

 

 

Limitations

 

`PChar` is not a native pointer to a C# string.

 

Important differences are:

 

C# strings are immutable.
`new PChar(string)` creates a writable copy.
`PChar.FromStringView(string)` is non-copying but read-only.
Existing `char[]` backing is not automatically null-terminated.
Pointer writes never resize the backing.
Delphi string headers and memory stored before the character data are not exposed.
`PChar` should be used for UTF-16 character data, not arbitrary binary data.

 

Use `Pointer`, `UntypedPointer`, or `Pointer<T>` for binary buffers and record data.

 

 

 



This page belongs to the Delphi2C# Documentation

Delphi2C# home  Content