从字节数组创建字符串

时间:2013-12-17 08:34:08

标签: string delphi stack dynamic-arrays delphi-xe4

我正在尝试使用下面的代码中的StringOf函数创建字符串。为什么在用于创建字符串ZeroMemory的数组上Showmessage之后没有显示任何内容。为什么?在评论ZeroMemory中显示案例=====

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;

我正在使用Delphi XE4

我尝试ZeroMemory的原因是我不会百分之百地保证新创建的字符串不使用对byte []的引用,而是复制b数据。在ZeroMemory的帮助下,我正在删除b的内容,同时期望它不会对字符串产生影响。

3 个答案:

答案 0 :(得分:2)

ZeroMemory没有释放内存。它将零字节写入您提供的内存块。

即使这样,您的代码也会出错。在您的代码中,b是指向动态数组的指针。您将@b传递给ZeroMemory,因此您将指针归零而不是指向它的数组。由于您传递的值字节数大于SizeOf(b),因此您也将堆栈的其他部分归零。这就是你打电话给ZeroMemory正在摧毁你的字符串的原因。

将你要写的内存归零:

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

如果要删除动态数组,则可以编写

b := nil;

Finalize(b);

SetLength(b, 0);

  

我尝试使用ZeroMemory的原因是我想100%确定新创建的字符串不使用对字节数组的引用,而是它的副本。

您无需编写任何代码来证明这一点。您可以肯定,因为Delphi字符串是UTF-16编码的,并且您的字节数组使用8位编码。因此,即使RTL设计人员想要对字节数组进行引用,也是不可能的。

答案 1 :(得分:1)

你刚刚在那里吹掉了堆栈变量。 B指针(完整)和部分指针S(Length(b) - SizeOf(b))字节)。

什么是b?它是一个复杂的结构,一个句柄,一个指针。通常你不想破坏内存结构,你想把数据放入单元格。但在您的示例中,您只是清除了堆栈上分配的整个内存结构。可能包括字符串本身。

以下程序在Delphi XE2中按预期工作 - 看看有什么而不​​是Zero Memory。当你想使用低级技巧作为原始指针(或ZeroMemory中的无类型变量)时,阅读Delphi中的动态数组以及如何从CPU Assembler的角度分配它们

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.

参见手册。

所以下一个问题是你为什么要打电话给ZeroMemory,有什么意义呢?是否有soem试图销毁密钥或其他敏感数据? http://www.catb.org/~esr/faqs/smart-questions.html#goal

如果您只想确保“s”变量没有任何外部引用 - 它有一个特殊的函数,UniqueString

然而,在这个特定的工作流程中,这个特殊的Delphi版本无论如何都不会发生。再次阅读StringOf的手册 - 它会返回一个UnicodeString临时隐藏变量。该变量在XE4中以UTF-16编码,这意味着每个字母有2个字节,这意味着原始的字节链无论如何都不适合,并且会被转换为新的缓冲区。

之后,将UnicodeString临时隐藏变量转换为每个字母有一个字节的AnsiString变量s,因此它也不能引用temp-var,但会分配另一个独立的缓冲区来保存转换的数据。

正如您所看到的,有两个必要的复制转换操作,这两个操作都使得保持数据引用变得不可能。

答案 2 :(得分:1)

你可能想这样做:

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

而不是

ZeroMemory(@b, Length(b));

请记住,b变量仅为4字节大小的指针,并指向字节数组。