“现在打包强制字节对齐记录”是什么意思?

时间:2011-12-10 23:53:27

标签: delphi delphi-xe2

Delphi XE2的新功能包含following

  

现在打包强制记录的字节对齐

     

如果您的遗留代码使用了打包记录类型并且您想要   要链接外部DLL或C ++,您需要删除该单词   从您的代码中“打包”。 packed关键字现在强制字节对齐,   而在过去它不一定这样做。行为   更改与Del​​phi中的C ++对齐兼容性更改有关   2009。

我不明白这一点。我正在努力解决这一问题:而在过去它并不一定会这样做。我无法调和的是,压缩总是导致记录的字节对齐,据我所知。任何人都可以给出一个非字节对齐的打包记录的例子吗?显然,这必须是早期版本。

为什么文档会说“如果你想与外部DLL或C ++链接,你需要删除代码中的单词”?如果外部代码使用#pragma pack(1),那么如果打包不受限制我们该怎么办?

$ALIGN指令怎么样? {$A1} and {$A-}等同于packedpacked是否有额外含义?

似乎我错过了一些东西,如果有人能够解释这一点,我会很感激。或者文档真的很差?

更新

我有理由相信文档指的是记录本身的 alignment 而不是记录的 layout 。这是一个小程序,它表明在记录上使用packed会强制记录的对齐为1。

program PackedRecords;
{$APPTYPE CONSOLE}
type
  TPackedRecord = packed record
    I: Int64;
  end;

  TPackedContainer = record
    B: Byte;
    R: TPackedRecord;
  end;

  TRecord = record
    I: Int64;
  end;

  TContainer = record
    B: Byte;
    R: TRecord;
  end;

var
  pc: TPackedContainer;
  c: TContainer;

begin
  Writeln(NativeInt(@pc.R)-NativeInt(@pc.B));//outputs 1
  Writeln(NativeInt(@c.R)-NativeInt(@c.B));//outputs 8
  Readln;
end.

这在Delphi 6,2010,XE和XE2 32位和XE 64位上产生相同的输出。

4 个答案:

答案 0 :(得分:5)

文档的最新更新删除了此问题所基于的所有文本。我的结论是原始文本只是一个文档错误。

答案 1 :(得分:2)

由于我不是Delphi编译器,我也可以像其他人一样猜测:记录对齐可能不是用于对齐记录中的成员,而是记录本身。

如果声明一个记录变量,它与内存中的某个地址对齐,那很可能是在4字节边界上对齐的。对于打包和解包的记录,情况就是如此(在D2007中测试过)。

现在在XE2中,打包记录放在1字节边界,而解压缩记录放在某个偶数边界上,可以通过align关键字控制。像这样:

type
  TRecAligned = record
    b1: byte;
    u1: uint64;
  end align 16;

  TRecPackedAligned = packed record
    b1: byte;
    u1: uint64;
  end align 16;

打包记录仍然在1字节边界上对齐,而解压缩记录与16字节边界对齐。

正如我所说,这只是猜测。 Embarcadero引用的措辞并不是很清楚。

答案 2 :(得分:1)

据我记忆,自从Delphi 5-6版本的编译器版本以来,record曾被打包过。

然后,出于性能原因,根据项目选项中的设置对齐了普通record字段。如果您定义packed record,则记录中将不会有任何对齐。

您引用的文字似乎与XE2无关,而是与Delphi 2009更改有关。有关历史目的,请参阅this Blog entry

我猜“'打包'现在的力量字节对齐记录”是指Delphi 2009 {$OLDTYPELAYOUT ON}功能 - 在XE2之前可能会遇到一些实施问题。我同意你的看法:这听起来像是一个文档问题。

答案 3 :(得分:-1)

pascal一直支持打包结构,这可能是使用示例

最简单的解释

假设您有两个数组x和y,并且我们使用的是16位处理器。也就是说,'字'处理器的大小是16位。

myrec = 
record
  b : byte;
  i : integer;
end;

x : array[1..10] of myrec;
y : packed array[1..10] of myrec;

Pascal只告诉你有10个元素可以使用,每个元素都有3个字节的信息(假设整数是旧的16位变量)。 它实际上没有说明如何存储该信息。 您可以假设数组存储在30个连续的字节中,但这不是必需的,并且完全依赖于编译器(这是避免指针数学的一个很好的理由)。

编译器可能会在字段值b和i之间放置一个虚拟字节,以确保b和i都落在字边界上。 在这种情况下,结构总共需要40个字节。

'包装'保留字指示编译器针对大小超速进行优化,而在打包时,编译器通常会优化速度而不是大小。 在这种情况下,结构将被优化,并且可能只需要30个字节。

我再次说'可能'因为它仍然依赖于编译器。 '包装'关键字只是说要将数据打包在一起,但实际上并没有说明如何。 因此,一些编译器只是忽略了关键字,而其他编译器则使用打包来禁用字对齐。

在后者的情况下,通常可能发生的是每个元素与处理器的字大小对齐。 你需要在这里意识到处理器的字大小通常是寄存器大小,而不仅仅是“16位”。因为它通常意味着。例如,在32位处理器的情况下,字大小为32位(即使我们通常将字定义为16位 - 这只是历史回归) 从某种意义上说,它是“记忆”和“记忆”。等同于磁盘扇区。

通过打包数据,您可能会有一些值,例如跨越字边界的整数,因此需要更多的内存操作(就像记录穿过扇区边界时需要读取两个磁盘扇区一样)。

所以基本上改变意味着,delphi现在完全使用packed关键字并且一直强制字节对齐(字对齐)。

希望这足以解释它。这实际上是一个更大的话题,我可以在几个段落中合理地回答。


更新,继续下面的评论主题......

DavidH>我无法想象你的意思是编译器能够在给定相同输入的情况下产生不同的输出

是大卫,这正是我所说的。不一定在同一个编译器上从一个编译到下一个编译器,但肯定在编译器的版本,编译器的品牌和不同的平台之间。

TonyH>我认为我们大多数人总是假设打包=字节对齐,所以现在我们正在摸索它没有的情况。 @David Heffernan问是否有人知道一个

托尼,你在这里击中了钉子。 您假设打包的工作类似于编译器指令,该指令指定数据存储器的组织方式。 这不是它的意思。更好的类比是考虑用于代码生成的优化指令。 您知道代码正在优化,但您不必具体了解底层输出代码的内容,也不需要。

我可以举出很多例子。 假设您有以下内容(我使用的是数组,它可以轻松应用于记录)

myarray = packed array[1..8] of boolean

如果您再打印一个SizeOf(myarray),您会期望什么? 答案是delphi不保证打包以任何特定方式实现。 因此,答案可能是8个字节(如果您对每个元素进行字节对齐)。 对于delphi而言,将这些作为位包装同样有效,因此整个数组可以适合1个字节。 如果编译器决定不优化字节边界并且运行在16位架构上,则可能是16个字节。 是的,它在速度方面效率较低,但包装的重点是优化尺寸超速。 只要它们只是访问数组元素并且不做任何假设并尝试使用自己的指针数学来访问底层数据,它对开发人员来说是不可见的。

同样,您甚至无法保证基元的内部组织 - 它依赖于平台,而不依赖于编译器。例如。如果您已经跨多个体系结构工作,那么您将意识到围绕多少位构成整数的问题;是否以字节顺序或反向字节顺序存储该整数内的字节。

这些类型的体系结构问题正是为什么开发了各种技术(如逗号分隔文件,xml等)的原因。为了提供可靠的通用平台。

总结一下,大卫的问题是你实际上是在问错误的问题。如果你在我所说的内容中考虑Embarcadero的引用,你会发现它与我所说的一致。