Pergunta

I'm trying to create string using StringOf function in code below. Why after ZeroMemory on array that was used to create string Showmessage displays nothing. Why? In commented ZeroMemory case ===== is displayed.

TIdBytes = array of Byte;

procedure fill(var b: TIDBytes);
begin
setlength(b,5);
b[0]:=61;
b[1]:=61;
b[2]:=61;
b[3]:=61;
b[4]:=61;
end;


procedure TMainForm.FormCreate(Sender: TObject);
var
POSSaleTransaction: TPOSSaleTransaction;
s: ansistring ;
b:TIDBytes;
begin
fill(b);
s := StringOf( TArray<byte>(b) );
ZeroMemory(@b, Length(b));
Showmessage(s);
end;

I'm using Delphi XE4

The reason I'm trying to ZeroMemory is that I wont to be 100% shure that newly created string is not using reference to byte[], but copyes b data. With help of ZeroMemory I'm deleting contents of b while expecting that it will not have influence on string.

Foi útil?

Solução 2

You just blew the stack variables there. Both the B pointer (in its entirety) and partially the pointer S ( (Length(b) - SizeOf(b)) bytes of it ).

What is b ? it is a some complex structure, a handle, a pointer. Usually You do not want to destroy memory structure, you want to put the data into the cells. But in your example you just wiped out the whole memory structures allocated on stack. Including, probably, the string itself.

The following program works as expected in Delphi XE2 - see what is there instead of Zero Memory. Read what are dynamic arrays in Delphi and how they are allocated from CPU Assembler point of view when you want to use low-level tricks as raw pointers ( or untyped variables like in ZeroMemory)

program Project11;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils;

procedure fill(var b: TBytes); 
begin
  SetLength(b,5);

//   b[0]:=61; b[1]:=61; b[2]:=61; b[3]:=61; b[4]:=61;
  FillChar(b[Low(b)], Length(b), 61); // Less copy-paste, more program structure
//  Notice, above i take pointer to the cell inside the array, 
//    not to the array the container itself. 
//  That is both safer and does document the intention of the code
end;

Procedure SOTest();
var
  s: ansistring ;
  b: TBytes;
begin
  fill(b);
  s := StringOf( b );

//  ZeroMemory(@b, Length(b)); -- destroying the pointer instead of freeing memory - is a memory leak
//  FillChar(b, Length(b), 0); -- same as above, written in Pascal style, rather than C style.

  b := nil;  // this really does free the DYNAMIC ARRAYS. Laconic but prone to errors if mistyped.
// SetLength(b, 0); -- more conventional and safe method to do the same: free string or dyn-array.
// Anyway that is unnecessary - both b and s would anyway be auto-freed before the function exit.

  Writeln(Length(s):4, '  ', s);
end;


begin
  try
    { TODO -oUser -cConsole Main : Insert code here }
    SOTest;

    Write('Press Enter to exit;'); ReadLn;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.

See manuals.

So the next question is WHY were you trying to call ZeroMemory, what is the point there ? IS there soem attempt to destroy a cypher key or other sensitive data ? http://www.catb.org/~esr/faqs/smart-questions.html#goal

If you only want to assure that "s" variable does not have any external references - there is a special function for it, UniqueString.

However in this particular workflow and this particular Delphi version that could not happen anyway. Read again manual for StringOf - it returns a UnicodeString temporary hidden variable. That variable is encoded in UTF-16 in XE4, which means having 2 bytes per letter, which means the original byte-chain would not suit anyway and would be transformed into new buffer.

After that you convert the UnicodeString temporary hidden variable into AnsiString variable s having one byte per letter, so it also can not have references to the temp-var, but would allocate yet another independent buffer to hold the transformed data.

As you can see there is two necessary copy-with-transformation operations, both of which make keeping data references just impossible.

Outras dicas

ZeroMemory does not free memory. It writes zero bytes into the block of memory that you provide.

Even then, your code gets that wrong. In your code, b is a pointer to the dynamic array. You pass @b to ZeroMemory so you are zeroising the pointer rather than the array that it points to. And since the value byte count that you pass is greater than SizeOf(b) then you are zeroising other parts of the stack too. That's why your call to ZeroMemory is destroying your string.

To zeroise the memory you would write:

ZeroMemory(Pointer(b), Length(b));

If you want to delete a dynamic array then you can write

b := nil;

or

Finalize(b);

or

SetLength(b, 0);

The reason I'm trying to use ZeroMemory is that I want to be 100% sure that newly created string is not using reference to the byte array, but is a copy of it.

You don't need to write any code to prove that. You can be sure because a Delphi string is UTF-16 encoded, and your byte array uses an 8 bit encoding. So even if the RTL designers wanted to take a reference to the byte array, it would not have been possible.

You probably want to do this:

ZeroMemory(@b[0], Length(b));

instead of

ZeroMemory(@b, Length(b));

Remember the b variable is 4 bytes size pointer only and point to array of bytes.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top