Delphi XE3 -> 整数转换为字节数组

6

我有一个数据结构:

data = array of integer;

我从一个

填写了它
source = array of byte;

使用

data[x] := Source[offset] or (Source[offset + 1] shl 8) or
    (Source[offset + 2] shl 16) or (Source[offset + 3] shl 24);

在处理完这些块之后,我需要将它们转换回“字节”格式... 有什么好的想法吗?

为什么不直接使用Move来blit它们呢?如果有网络/主机字节顺序的更改,那么可以使用ntohl单独处理。 - David Heffernan
我同意@David的观点,虽然可以直接回答你的问题,但我感觉你应该重新考虑你的设计。 - Andreas Rejbrand
我这样做是因为只是从C++快速“移植”到Delphi。在C++中,第一个“转换”只是int data = (int)source,其中source被声明为unsigned char *source。 - Michael Grenzer
5个回答

8
你是指像这样吗?
var
  i: integer;
  b1, b2, b3, b4: byte;
begin
  b1 := byte(i);
  b2 := byte(i shr 8);
  b3 := byte(i shr 16);
  b4 := byte(i shr 24);

比如,尝试以下方法:

procedure TForm1.FormCreate(Sender: TObject);
var
  i: integer;
  b1, b2, b3, b4: byte;
begin
  i := $AABBCCDD;
  b1 := byte(i);
  b2 := byte(i shr 8);
  b3 := byte(i shr 16);
  b4 := byte(i shr 24);
  ShowMessage(IntToHex(b1, 2));
  ShowMessage(IntToHex(b2, 2));
  ShowMessage(IntToHex(b3, 2));
  ShowMessage(IntToHex(b4, 2));
end;

谢谢。我相信我没有看到森林,只看到了树木。 - Michael Grenzer
@Michael 这个答案是树。森林仍然在那里等着你去发现!我并不是冒犯安德烈亚斯,他在上面的评论中也提到了这一点。 - David Heffernan

7
您可以使用Move在一行命令中完成此操作。
Move(source[0], dest[0], Length(source)*SizeOf(source[0]));

如果需要执行网络/主机字节顺序转换,则可以在Move之后遍历整数数组。
反方向上,您需要按相反的顺序执行此操作。
如果没有字节顺序问题,则实际上可能根本不需要转换为字节数组。可能可以直接使用整数数组。请记住,在没有字节顺序问题的情况下,字节数组和整数数组的内存布局是相同的(这就是为什么可以使用Move进行块传输的原因)。

确实。但我感觉这些数据可能可以以任何形式用于任何事情,所以我怀疑原帖作者根本不需要复制任何东西... - Andreas Rejbrand
@Andreas 很有趣的时机。我得出了同样的结论并写了下来,然后看到了你的评论! - David Heffernan

7
哦...我看到有一个使用Move操作和另一个使用位移操作的答案,但是有没有一种简单的转换方式呢?
var
  I: Integer;
  B: array[0..3] of Byte;
begin
  // from bytes to integer:
  I := PInteger(@B)^;
  // from integer to bytes:
  PInteger(@B)^ := I;

或者使用您的数组:
data[i] := PInteger(@source[offset])^;

反之亦然:

// get low byte
source[offset] := PByte(@data[i])^; // or := PByte(@data[i])[0];
// get second byte
secondByte := PByte(@data[i])[1]; // or := (PByte(@data[i]) + 1)^;

或者

PInteger(@source[offset])^ := data[i];

如你所见,通过将其强制转换为指针,你可以获得很多便利。这并不实际获取指针,编译器足够聪明,可以直接访问项目。


敢提“绝对值”吗?这将完全消除赋值的需要(那些将整个内容向左或向右移动的赋值)。 - Andriy M
你不应该冒险;-) 在我看来,“absolute”是一种可憎的东西,应该从语言中删除。但如果你想讨论为什么,可以使用Embarcadero论坛。 - Rudy Velthuis
如果你不需要赋值,那就不要赋值。但在这种情况下是有需要的。如果没有必要,你可以轻松地使用相同的转换。我使用赋值来证明结果确实是一个字节或整数,具体取决于你的需求。 - Rudy Velthuis

0
如所述,您不需要将数据移动以访问它,因为字节和整数都可以使用类型转换访问。
通过类型转换,您可以将原始字节数组视为整数数组进行访问。
type
  TArrayInteger = array of Integer;
...
for i := 0 to Pred(Length(source)) div SizeOf(Integer) do
  WriteLn(TArrayInteger(source)[i]);

通常我会将这些类型转换隐藏在一个类中。在XE3中,可以为简单类型(如字符串、字节、整数等)声明类助手。例如,请参见TStringHelper。
对于简单类型的数组也是如此。
以下是使用记录助手的示例:
type
  TArrayByte = array of Byte;
  TArrayInteger = array of Integer;

  TArrayByteHelper = record helper for TArrayByte
  private
    function GetInteger(index : Integer) : Integer;
    procedure SetInteger(index : Integer; value : Integer);
  public
    property AsInteger[index : Integer] : Integer read GetInteger write SetInteger;
  end;

function TArrayByteHelper.GetInteger(index: Integer): Integer;
begin
  Result := TArrayInteger(Self)[index];
end;

procedure TArrayByteHelper.SetInteger(index: Integer; value: Integer);
begin
  TArrayInteger(Self)[index] := value;
end;

使用方法如下:

Var
  source : TArrayByte;
  i : Integer;
begin
  SetLength(source,8);
  for i := 0 to 7 do
    source[i] := i;
  for i := 0 to 1 do
    WriteLn(Format('%8.8X',[source.AsInteger[i]]));
  ReadLn;
end.

0
构建此函数的方法如下:
Type TBytes = array of byte;

function InttoBytes(const int: Integer): TBytes;
begin
  result[0]:= int and $FF;
  result[1]:= (int shr 8) and $FF;
  result[2]:= (int shr 16) and $FF;
end;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接