[fpc-pascal] Difference between string and Tbytes as procedure arguments?

Michael Van Canneyt michael at freepascal.org
Sun Nov 5 10:36:47 CET 2023



On Sun, 5 Nov 2023, Bo Berglund via fpc-pascal wrote:

> I am maintaining an old utility program that started out about 20+ years ago
> using Delphi7.
>
> The utility communicates via RS232 with a measuring system in order to retrieve
> and process recorded data and at the time this was started I used strings as
> buffers for the serial comm.
>
> A string at the time was really just an array of 1-byte characters but it had
> useful functions for manipulating data inside so it made sense to use strings at
> the time.
>
> But as time evolved and Borland changed the meaning of string to now be
> unicodestring it could no longer be used as a comm buffer, so I switched
> declaration to AnsiString or even RawByteString to keep the existing utility
> tools operational.
>
> New programs I wrote would use TBytes as buffer instead...
>
> Then about 6-7 years ago I had to do a major overhaul of the program in order to
> improve the user interface and add support for switching languages in the UI and
> at that time I also tried my best to replace the AnsiString buffers with TBytes
> buffers. At this time I was on Delphi XE5.
>
> It all looked OK until I recently got a call from a distributor who had
> discovered that for a certain type of seldom used data collection mode the
> output was corrupted when transfered to disk....
>
> And now I have found that the corruption happens inside a function that analyzes
> one of the data packets where it will remove the start header record from the
> buffer and then continue parsing the data items. (The buffer is sent in as a
> regular argument without the var specifier).
>
> Following this analysis in main code the buffer itself (that was used in the
> previous call) is saved to disk in binary format.
> And here is the problem:
>
> The saved image of the buffer lacks the header part (30 bytes)...
>
> So my question is this:
> Is there a difference in handling the arguments between string, AnsiString,
> RawByteString and TBytes in a declaration like this:
>
> function TSSConnection.ParseCmdFileData(var SSCmdFile: TSSCommandFile; Buf:
> AnsiString): boolean;
>
> and this:
>
> function TSSConnection.ParseCmdFileData(var SSCmdFile: TSSCommandFile; Buf:
> TBytes): boolean;
>
> In the first instance it looks like the function receives a *copy* of the data

No, but an ansistring is copy-on-write. As soon as you change it, a copy is
made if your routine is not the only one using the string.

> in the Buf argument and in the second case it receives a pointer to the actual
> live data such that in the first case the argument source remains untouched
> whereas in the second case the argument source is changed just as it had been
> declared as var...

That is correct. TBytes is a fancy wrapper around a pointer with some
reference counting added to the mix.

>
> Is there a way to simply tell Delphi/FreePascal to treat also TBytes as copy
> rather than a pointer to the real data?

You must copy the data. The copy() function can be used for this.

Michael.


More information about the fpc-pascal mailing list