7

I want to store to disk some data records. I want to have each data record of equal size so I can quickly compute and jump to a certain record. So, I store the only string in my record as an array of chars:

type TFileNameIO = array[1..512] of Char;

After I read the string from disk, the content of my string is like this:

c:\windows#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 etc

I want to truncate the #0 characters, and I do something like this:

function GetFileName(DiskName: TFileNameIO): string;
VAR PC: PChar;
begin
 SetString(Result, PChar(@DiskName), Length(DiskName));     <----- here Result is: c:\windows#0#0#0 etc
 PC:= PChar(Result);
 Result:= WideCharToString(PC);
end;

There is a better way to do it?

Gabriel
  • 20,797
  • 27
  • 159
  • 293

2 Answers2

8

You can assign a null-terminated PChar directly to a String:

function GetFileName(DiskName: TFileNameIO): string;
begin
 Result := PChar(@DiskName);
end;

Just make sure the TDiskName always contains a null terminator. If it can ever be completely full of characters without a null, you will have to do this instead:

function GetFileName(DiskName: TFileNameIO): string;
var
 Len, I: Integer;
begin
  Len := 0;
  For I := Low(DiskName) to High(DiskName) do
  begin
    if DiskName[I] = #0 then Break;
    Inc(Len);
  end;
  SetString(Result, PChar(@DiskName), Len);
end;
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • I get: "E2010 Incompatible types: 'string' and 'Pointer'" – Gabriel Aug 22 '15 at 22:55
  • I keep forgetting that Delphi's `@` operator is usually not typed, because [`{$TYPEDADDRESS}`](http://docwiki.embarcadero.com/RADStudio/XE8/en/Type-checked_pointers_(Delphi)) is OFF by default. – Remy Lebeau Aug 22 '15 at 22:58
  • If I add {$TYPEDADDRESS on} other code get broken. I need probably to check the entire code to fix the places where @ is used. – Gabriel Aug 22 '15 at 23:09
  • 1
    The `@` is needed in this situation, but you have to make sure you are assigning a `PChar` to the `string`, so either use `{$TYPEDADDRESS ON}` with `@DiskName[1]`, or use an explicit typecast with `PChar(@DiskName)`. – Remy Lebeau Aug 22 '15 at 23:12
3

Your function is not necessary because you can simply assign a character array to a string variable.

type
  TCharArray = array [1..512] of Char;
....
var
  arr: TCharArray;
  str: string;
....
arr := ...;
str := arr;

This results in str being assigned the contents of arr. The compiler implements this with a call to System._UStrFromWArray which first of all looks for a null-terminator. If it finds one, that determines the length of str. Otherwise, the entire contents of the array are copied, and Length(str)=Length(arr).

As a broader point, I would recommend that you change to using zero-based character arrays. From the documentation:

Zero-based character arrays are compatible with PChar and PWideChar. When you use a character array in place of a pointer value, the compiler converts the array to a pointer constant whose value corresponds to the address of the first element of the array.

This makes it easier for you to interop with functions that expect PChar arguments.

As an aside, passing TFileNameIO by value is inefficient because it involves a memory copy. Pass such types by reference. For instance as a const parameter.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • I never used a 1-based character array as a 0-based character. I would expect to write `TCharArray = array [0..511] of Char`. Does `array [1..512]` work in practice? +1 – Arnaud Bouchez Aug 23 '15 at 14:33
  • @Arnaud Thanks. The 1 based array supports assignment from character array to string. But it's not compatible with PChar. You can see from my edits that the first part was a surprise to me also. I personally would not contemplate anything other than zero based arrays. – David Heffernan Aug 23 '15 at 14:36
  • I thought that no copy will be made for DiskName. From what I understand ( https://stackoverflow.com/questions/9543472/delphi-passing-parameters-by-reference-or-by-value-copy ) a copy is created ONLY if I touch (write to/modify) DiskName. – Gabriel Jan 17 '20 at 10:06
  • That doesn't apply. In the code in the Q you pass a fixed length array by value. So a copy made and that is passed. – David Heffernan Jan 17 '20 at 11:08