首页 文章

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

提问于
浏览
21

Delphi XE2的新功能包含following .

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

我没有在这一点上挣扎:而在过去,它并不一定会这样做 . 我无法调和的是,压缩总是导致记录的字节对齐,据我所知 . 任何人都可以给出一个非字节对齐的打包记录的例子吗?显然,这必须是早期版本 .

为什么文档说"if you want to link with an external DLL or with C++, you need to remove the the word packed from your code"?如果外部代码使用 #pragma pack(1) 那么如果打包是禁止的,我们该怎么办?

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

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

Update

我有理由相信文档是指记录本身的对齐而不是记录的布局 . 这是一个小程序,它表明在记录上使用 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 回答

  • -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引用的措辞对于该项目并不是那么清楚 .

  • 5

    据我所知,自从Delphi 5-6的编译器版本以来, record 曾经被打包过 .

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

    您引用的文本似乎并非专门针对XE2,而是针对Delphi 2009的更改 . 有关历史记录,请参阅this Blog entry .

    我想“'Packed'现在Forces Byte Alignment of Records”是指Delphi 2009 {$OLDTYPELAYOUT ON} 功能 - 在XE2之前可能会有一些实现问题 . 我同意你的意见:这听起来像是一个文档问题 .

  • 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个字节 .

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

    我再次说'可能',因为它仍然依赖于编译器 . 'packed'关键字只是表示将数据打包得更近在一起,但实际上并没怎么说 . 因此,一些编译器只是忽略了关键字,而其他编译器则使用打包来禁用字对齐 .

    在后者的情况下,通常可能发生的是每个元素与处理器的字大小对齐 . 你需要在这里意识到处理器的字大小通常是寄存器大小,而不仅仅是'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的引用,你会发现它与我所说的一致 .

相关问题