I would write this function as follows:
procedure Concat(var Dest: string; const Source: array of string); var i: Integer; OriginalDestLen: Integer; SourceLen: Integer; TotalSourceLen: Integer; DestPtr: PChar; begin TotalSourceLen := 0; OriginalDestLen := Length(Dest); for i := low(Source) to high(Source) do begin inc(TotalSourceLen, Length(Source[i])); end; SetLength(Dest, OriginalDestLen + TotalSourceLen); DestPtr := PChar(Pointer(Dest)) + OriginalDestLen; for i := low(Source) to high(Source) do begin SourceLen := Length(Source[i]); Move(Pointer(Source[i])^, DestPtr^, SourceLen*SizeOf(Char)); inc(DestPtr, SourceLen); end; end;
This is pretty clear. Complications are caused by blank lines. Any attempt to index empty string characters will throw an exception if range checking is enabled.
To deal with this complexity, you can add if tests for the case when one of the lines involved in the Move call is empty. I prefer a different approach. I prefer the string variable to be a pointer. This bypasses the range check, but also allows you to omit the if .
Move(Pointer(Source[i])^, DestPtr^, SourceLen*SizeOf(Char));
You might think what happens if Source[i] empty. In this case, Pointer(Source[i]) is nil , and you can expect an access violation. In fact, there is no error, because the length of the movement specified by the third argument is zero, and the nil pointer is never canceled.
Another note here:
DestPtr := PChar(Pointer(Dest)) + OriginalDestLen;
We use PChar(Pointer(Dest)) , not PChar(Dest) . The latter calls the code to check if Dest empty or not, and if it gives a pointer to one null terminator. We want to avoid executing this code and get the address located in Dest directly, even if it is nil .
source share