【问题标题】:Creating string from byte array从字节数组创建字符串
【发布时间】:2014-01-04 22:21:33
【问题描述】:

我正在尝试在下面的代码中使用StringOf 函数创建字符串。为什么在用于创建字符串Showmessage 的数组上的ZeroMemory 之后什么都不显示。为什么?在已注释的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 的原因是我不会 100% 确定新创建的字符串不使用对 byte[] 的引用,而是复制 b 数据。在 ZeroMemory 的帮助下,我正在删除 b 的内容,同时期望它不会对字符串产生影响。

【问题讨论】:

  • ZeroMemory(@b, Length(b)); 这不是擦除指针而不是释放内存的内存泄漏吗?
  • 我正在使用 Delphi XE4。
  • 在这种特殊情况下如何释放内存?
  • 为什么要关心 5 字节的内存?它将在程序退出时被释放。就像s 会被释放一样。你不问如何释放s 内存,那为什么还要问b?但是您可以调用SetLength(..., 0); - 两者都使用sb - 或者如果您想提前释放这些缓冲区,您可以将数组分配给nil(这不适用于字符串AFAIR)。
  • 无论如何,ZeroMemory 从来没有释放内存,它总是破坏数据并用零覆盖它。不知道谁告诉你 ZeroMemory 可以释放任何东西,他不是在跟你开玩笑。 // FormCreate 中的类型转换也有些不准确。正如您在手册中看到的,StringOf 自然适用于另一种类型,TBytesdocwiki.embarcadero.com/Libraries/XE5/en/…

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


【解决方案1】:

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 设计者想要引用字节数组,也是不可能的。

【讨论】:

    【解决方案2】:

    你只是把堆栈变量放在那里了。 B 指针(整体)和部分指针 S(它的 (Length(b) - SizeOf(b)) 字节)。

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

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

    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,这有什么意义?是否有人试图破坏密码密钥或其他敏感数据? http://www.catb.org/~esr/faqs/smart-questions.html#goal

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

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

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

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

    【讨论】:

    • 我尝试 ZeroMemory 的原因是我不会 100% 确定新创建的字符串不使用对 byte[] 的引用,而是复制 b 数据。在 ZeroMemory 的帮助下,我正在删除 b 的内容,同时期望它不会对字符串产生影响。
    • 如果 ZeroMemory 破坏了 b 的指针,那为什么它也会破坏字符串呢?
    • 因为自Length(b) &gt; SizeOf(b)以来,您破坏的不仅仅是指针
    • @user1501700 如果您想确保字符串被隔离,没有共享数据 - 只需调用正确的函数,UniqueString docwiki.embarcadero.com/Libraries/XE5/en/System.UniqueString 但在这种情况下,字符串将被隔离,请考虑:1st您将字节链转换为 UTF-16 字符串(UTF-16 使每个字母为 2 个字节,因此引用不会这样做),然后将 UTF-16 搅拌转换为“每个字符 1 个字节”AnsiString。在这些转换过程中,仅传递数据,而不是对其的引用。
    • @user1501700 在我的示例中,它只是破坏了指向随机指针的 S 指针,并试图读取它导致访问冲突。如果对你来说它只是默默地显示一个空字符串而不是 AV - 那很奇怪
    【解决方案3】:

    你可能想这样做:

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

    而不是

    ZeroMemory(@b, Length(b));
    

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

    【讨论】:

      猜你喜欢
      • 2020-09-15
      • 1970-01-01
      • 1970-01-01
      • 2016-03-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多