Posted on September 6th, 2008 in Castalia, CodeGear, Delphi | No Comments »
Yesterday, I wrote about the new UnicodeString type in Delphi 2009, and the fact that the Char type is now a 2-byte Char instead of a 1-byte Char. I wrote about how this can affect calls to SizeOf and Length, among other things.
While porting my code to Delphi 2009, I found a few instances where the changes introduced some memory management issues. Of course, the root cause of the problems were that my code assumed that SizeOf(Char) = 1, which is no longer true.
When that change was made, some decisions had to be made about certain parts of the VCL (or, more correctly, the RTL) and what parameters they would expect. Most of Delphi’s memory management routines expects parameters in numbers of Bytes, not Chars:
A common way to use FillChar has long been FillChar(memoryBuffer, Length(memoryBuffer), 0). This should fill memoryBuffer with zeroes. However, if memoryBuffer is an array of Char, this won’t work any more. It will only fill half of the array, because Length() returns the number of elements in the array, not the size of the array in bytes. The solution is to use SizeOf() instead of Length():
FillChar(memoryBuffer, SizeOf(memoryBuffer), 0);
Also, note that FillChar fills memoryBuffer with BYTES, not CHARS, even if the buffer is an array of Char. If your code reads FillChar(memoryBuffer, SizeOf(memoryBuffer), #36), your chars will be $3636, not $36 (AKA $0036) as you might have intended. To get a UnicodeString full of #36, you’ll need to use StringOfChar(). The analogous code to the above call of FillChar() is as follows:
StrPCopy(memoryBuffer, StringOfChar(#36, Length(memoryBuffer)));
Note that StringOfChar() takes the number of Char elements, not the size in bytes (hint for remembering: if the routine is specifically for strings, it probably takes the number of Char elements. If it’s a generic memory-management routine, it probably takes the number of bytes).
Move() can have the same problems as FillChar(). If you’re using Move() with Char arrays, Length() won’t work the way it used to:
Move(charArray1, charArray2, Length(charArray1));
Move is a generic memory management routine, so it expects the number of BYTES to move, not the number of Chars. Here’s the right way:
Move(charArray1, charArray2, SizeOf(CharArray1));
Alternatively, you could do Move(charArray1, charArray2, Length(charArray1) * SizeOf(Char)), but I think that’s unnecessary. Of course, if you feel it’s more readable, it will work just as well.
Copy() is related to Move(), though it’s aimed specifically at strings and arrays. This means that when using Copy(), you should pass in the INDICES of the elements (probably CHAR elements, if they’re potentially strings), and NOT the byte offset of the elements you want to copy. This isn’t as likely to be an issue, but it did bite me once while porting Castalia to Delphi 2009.
I hope that’s enough for one day. On Monday, we’ll look at how TStream descendants might present some problems.