我正在尝试使用下面的代码中的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
的内容,同时期望它不会对字符串产生影响。
答案 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字节大小的指针,并指向字节数组。