Delphi 7 与 2009 年(和 2010 年)相比创纪录大小

发布于 2024-09-08 20:42:38 字数 827 浏览 3 评论 0原文

将代码从 Delphi 7 转换为 2010 时,我遇到了一个奇怪的问题。它与记录有关。下面定义的记录在 D7 中调整大小时为 432 字节,在 D2009(和 2010)中为 496 字节。我知道,一个简单的解决方案是使其成为打包记录,然后所有版本都为 426 字节...但是,我们在流式传输记录的位置存储了数据,现在我们正在尝试使用更新的语言读取这些流。

TToTry = Record
 a,b,c,d : Extended;
 e,f,g,h : Extended;
 i : String[15];
 j,k,l,m,n,o,p,q,r,s,t : Array[1..3] of Extended; End;

在调查这个问题时,我创建了另一条记录,并且出于某种原因,大小是相同的?记录较小,但具有相同的数据类型。但在该语言的所有版本中它的大小都是相同的。

TMyRecord = Record
Ext1  : Extended;
Ext2  : Extended;
Ext3  : Extended;
Ext4  : Extended;
Ext5  : Extended;
Ext6  : Extended;
Int1  : Integer;
Int2  : Integer;
char1 : AnsiChar;
char2 : AnsiChar;
MyString  : String[15];
Arr1  : Array[1..3] of Extended;
Arr2  : Array[1..3] of Extended; end;

有人知道为什么一张记录如此不同,而另一张记录却相同吗?肯定与 Delphi 中的字节边界对齐有关。但从一个版本到下一个版本,是什么发生了如此巨大的变化呢?

I have a weird issue when converting code from Delphi 7 to 2010. It has to do with records. The record defined below, when sized in D7, is 432 bytes, and in D2009 (and 2010) it's 496. I know, that an easy solution is to make it a packed record, then all versions come out to 426 bytes... However, we have data stored where we streamed the record and now we are trying to read those streams with a newer language.

TToTry = Record
 a,b,c,d : Extended;
 e,f,g,h : Extended;
 i : String[15];
 j,k,l,m,n,o,p,q,r,s,t : Array[1..3] of Extended; End;

In investigating this issue, I created another record, and, for whatever reason, the sizes are the same? The record is smaller, but it has the same data types. but it comes out the same size in all versions of the language.

TMyRecord = Record
Ext1  : Extended;
Ext2  : Extended;
Ext3  : Extended;
Ext4  : Extended;
Ext5  : Extended;
Ext6  : Extended;
Int1  : Integer;
Int2  : Integer;
char1 : AnsiChar;
char2 : AnsiChar;
MyString  : String[15];
Arr1  : Array[1..3] of Extended;
Arr2  : Array[1..3] of Extended; end;

Anybody have any insight as to why one record is so different, and the other is the same? Something to do with byte boundary alignments in Delphi for sure. but what changed so drastically from one version to the next?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

顾挽 2024-09-15 20:42:38

嗯,第一个问题是您将非打包记录存储到磁盘上。字段和数组封装允许在产品版本之间进行更改,因为通常内存中的布局在进程之外不可见。你已经违反了这条规则。

如果在 Delphi 7 和 Delphi 2009 之间字节填充默认值发生了变化,请找出 D7 中的默认值并将默认值设置为 Delphi 2009 中的相同值。

还要检查数组填充默认值。我不记得是否有单独的设置。

在 Delphi 2009 的调试内存视图中查看记录结构。部分或全部额外大小可能是由于记录本身(而不是其中的字段)的填充造成的,因此当在数组中使用记录时,数组元素位于快速机器边界上。

如果这些都没有帮助,请在 D2009 中创建临时打包记录类型,并在实际数据字段之间手动插入字节填充字段,直到记录大小和字段对齐与 D7 布局匹配。这不仅仅是大小,还有字段对齐。使用此临时打包记录读取旧数据文件。然后将数据逐字段传输到 D2009 中的“真实”记录类型中,并写出一个新文件。

当您执行此操作时,将该记录类型打包到 D2009 中,这样就不会再发生这种情况。

Well, the first problem is that you stored a non-packed record to disk. Field and array packing is allowed to change between product releases because normally the layout in memory is not visible outside of the process. You've broken that rule.

If the byte padding defaults changed between Delphi 7 and Delphi 2009, find out what the defaults were in D7 and set the defaults to the same in Delphi 2009.

Also check the array packing default. I can't remember if there is a separate setting for this.

Take a look at your record structure in Delphi 2009 in the debug memory view. Some or all of the additional size may be due to padding of the record itself (not the fields within it) so that when the record is used in an array the array elements are on speedy machine boundaries.

If none of this helps, create a temporary packed record type in D2009 and manually insert byte padding fields between the actual data fields until the record size and field alignments match the D7 layout. It's not just size, it's field alignments. Read your old data file in using this temp packed record. Then transfer the data field by field into your "real" record type in D2009 and write out a new file.

And while you're at it, pack that record type in D2009 so this doesn't happen again.

玩套路吗 2024-09-15 20:42:38

我相信您已经找到了一个功能!我无法使用 D2007 的 TToTry 获得合理的大小,因此我必须使用调试器查找字段地址;

首先,以下记录的大小

{$A8}
type
  TToTry = record
    j: array[1..3] of Extended;
    k: array[1..3] of Extended;
    l: array[1..3] of Extended;
    m: array[1..3] of Extended;
  end;

128 (32*4)。这是预期的,因为扩展是 10 字节,30 字节将对齐 32 字节。

但该记录的大小

{$A8}
type
  TToTry = record
    j, k, l, m: array[1..3] of Extended;
  end;

120 (30*4)。这当然是出乎意料的——字段仍应在 8 字节边界上对齐。

(我没有D7来验证,但我的想法是:)

现在我们知道分组字段是打包的,因此 D7 上的对齐是 8 个字节,并且您的记录几乎是打包的;

TToTry = Record 
 a,b,c,d : Extended;    // 40 bytes (8*5)
 e,f,g,h : Extended;    // 40 bytes (8*5)
 i : String[15];        // 16 bytes (8*2)
 j,k,l,m,n,o,p,q,r,s,t: Array[1..3] of Extended; // 330 bytes
End; 

编译器将 6 个字节填充到最后一组,使其成为 8 的倍数,然后得到 40+40+16+336 = 432 字节。

使用 D2009/D2010,您要么声明每个字段 - 而不对它们进行分组,要么行为发生改变。无论哪种方式打包您的记录并在末尾添加一个 6 字节数组虚拟字段,您就可以开始了。

如果这不起作用,请使用 D7 查看记录的字段地址,然后使用打包记录并根据需要使用虚拟字段在 D2009 上创建精确的副本,导入存储的数据后,您可以删除虚拟字段。

--

我从来不知道这种行为,也找不到任何地方的记录。尽管如此,它仍然非常像一个功能,以至于我不太愿意将其称为错误。我不知道该行为与 D2009 或 D2010 是否相同,请测试一下是否相同。如果是,为了获得预期结果(不要有半打包记录),不要偷懒,为非打包记录单独声明每个字段。

I believe you've hit a feature!. I could not get a reasonable size with your TToTry with D2007 so I had to look up field addresses with the debugger;

First, size of the below record,

{$A8}
type
  TToTry = record
    j: array[1..3] of Extended;
    k: array[1..3] of Extended;
    l: array[1..3] of Extended;
    m: array[1..3] of Extended;
  end;

is 128 (32*4). This is expected, since an Extended is 10 bytes, 30 bytes would align on 32 bytes.

But size of this record,

{$A8}
type
  TToTry = record
    j, k, l, m: array[1..3] of Extended;
  end;

is 120 (30*4). This is certainly unexpected - fields should still align on an 8 byte boundary.

(I don't have D7 to verify but my thinking is that:)

So now we know that grouped fields are packed, it follows that the alignment on D7 is 8 bytes and your record is nearly packed;

TToTry = Record 
 a,b,c,d : Extended;    // 40 bytes (8*5)
 e,f,g,h : Extended;    // 40 bytes (8*5)
 i : String[15];        // 16 bytes (8*2)
 j,k,l,m,n,o,p,q,r,s,t: Array[1..3] of Extended; // 330 bytes
End; 

The compiler is padding 6 bytes to the last group to have it a multiple of 8 and then you get 40+40+16+336 = 432 bytes.

With D2009/D2010 you are either declaring each field - without grouping them, or the behavior is changed. Either way pack your record and add a 6 byte array dummy field to the end, and you should be good to go.

If that does not work, look at the field addresses of your record with D7, then create an exact duplicate on D2009 by using a packed record and using dummy fields as necessary, after you've imported your stored data you can drop the dummy fields.

--

I've never known such behavior and I can't find it documented anywhere. Still, it is so much like a feature that I'm hesitant to call it a bug. I don't know the behavior is the same with D2009 or D2010, test to see if it is. If it is, to get expected results - not to have half-packed records - don't be lazy and declare each and every field on its own for non-packed records.

凹づ凸ル 2024-09-15 20:42:38

我知道这是一篇旧文章,但昨天我在 Delphi XE2 中遇到了同样的问题。我有几个使用 Turbo Delphi 2006 应用程序编写的键入文件,并且我还犯了不使用打包记录的错误。我的情况很复杂,因为我使用的是可变长度记录(这就是我们在 z/OS 世界中对它们的称呼,不是 100% 确定的 Delphi 术语),因此未对齐导致记录键标签最终没有出现在正确的字段,从而导致没有子记录位于正确的位置,从而导致整个文件无用。

我发现您可以将对齐编译器指令放在特定的代码块周围。除此之外,我还发现了这个小宝石: {$OLDTYPELAYOUT ON}

{$OLDTYPELAYOUT ON}
 RMasterRecord = Record       
    mKey       : word;
    mDeleted   : boolean;
    case mType : ANSIChar of
       'V' : //Info
          (Vers : RVersionLayout);
       […]
{$OLDTYPELAYOUT OFF}

我只将指令放在写入和读取文件的主记录周围,而不是放在子记录定义周围。这解决了我的问题,我现在可以在 XE2 中编译并读取我的 TD2006 文件。下次我将使用打包记录(或者更好的是 SQLite)。但我想我会分享这个,因为这个网站多年来为我提供了不可估量的帮助。

您可以在此处阅读有关 $OLDTYPELAYOUT 的更多信息:http://docwiki.embarcadero.com/ RADStudio/XE5/en/Internal_Data_Formats#Record_Types。顺便说一句,第一个修复它的是 {$A4} 但当我发现 {$OLDTYPELAYOUT ON} 时,我将其切换为使用它,因为它更明显。

I know this is an old post, but I ran into the same issue with Delphi XE2 yesterday. I have several typed files that were written out using a Turbo Delphi 2006 app and I also made the mistake of not using Packed records. My situation was compounded because I was using variable length records (that's what we call them in the z/OS world, not 100% sure of the Delphi term), so the mis-alignment was causing the record key tags to not end up in the correct field, thus causing none of the sub-records to be in the right place, rendering the entire file useless.

What I discovered is that you can put the alignment compiler directives around just a specific block of code. Along with that, I also discovered this little gem: {$OLDTYPELAYOUT ON}

{$OLDTYPELAYOUT ON}
 RMasterRecord = Record       
    mKey       : word;
    mDeleted   : boolean;
    case mType : ANSIChar of
       'V' : //Info
          (Vers : RVersionLayout);
       […]
{$OLDTYPELAYOUT OFF}

I only put the directive around the master record that gets written and read to the file, not around the sub-record definitions. This solved my issue, and I can now compile in XE2 and read my TD2006 files. Next time I will use Packed records (or better yet, SQLite). But I thought I would share this, since this site has helped me immeasurably for years.

You can read more about $OLDTYPELAYOUT here: http://docwiki.embarcadero.com/RADStudio/XE5/en/Internal_Data_Formats#Record_Types. By the way, the first one that fixed it was {$A4} but when I discovered {$OLDTYPELAYOUT ON} I switched it to use that, since it is more obvious.

拥有 2024-09-15 20:42:38

我相信默认对齐方式变宽了。在以后的版本中指定对齐方式 4,看看它是否按照您想要的方式显示。

将来,您应该确保任何要写入磁盘的记录都以打包方式存储,这样您就不会被这种方式烧毁。

编辑:由于没有任何对齐方式起作用(这让我感到惊讶),我会回到原始版本并弄清楚它是如何真正对齐的。用类似 $FF 的内容填充记录,将数据放入并写出 - 查看 $FF 幸存的位置。获取新记录,对其进行打包并添加填充符以匹配旧记录中的填充。

一件事:这实际上只是一张记录吗?过去,我使用对象作为带有继承的假记录——哎呀,在继承时应用了正常对齐,我无法阻止它。我最终不得不在数据之前进行填充,以使强制对齐不会破坏我的数据。 (这是一个 API,它必须是对的,我无法独立处理这些字段。)

I believe the default alignment was made wider. Specify alignment 4 in the later versions and see if it comes out the way you want.

For the future you should make sure that any record that is going to be written out to disk is stored packed so you don't get burned this way.

Edit: Since none of the alignments work (which surprises me) I would go back to the original and figure out how it really is aligned. Fill the record with something like $FF, put the data in and write it out--see where the $FFs survived. Take the new record, make it packed and add fillers to match the padding in the old record.

One thing: Is this actually just one record? In the old days I have used objects as fake records with inheritance--oops, at the point of the inheritance the normal alignment was applied and I couldn't stop it. I ended up having to pad before the data in order to make the forced alignment not break my data. (This was going to an API, it HAD to be right, I couldn't process the fields independently.)

屋檐 2024-09-15 20:42:38

应用 {$A8} 指令并不意味着所有记录字段都在 8 字节边界对齐 - 编译器使用不同的对齐策略。例如,

{$A8}
type
  TToTry = record
    a: byte;
    b: word;
    c: longword;
  end;

Delphi 2009中的大小为8字节,因为编译器将2字节值在2字节边界对齐,4字节值在4字节边界对齐,而上例中唯一实际对齐的是b字段对齐在 2 字节边界。

至于最初的问题 Delphi 7 和 Delphi 2009 之间发生了什么变化 - 请阅读 Sertac Akyuz 的回答和我的评论

Applying {$A8} directive does not mean that all record fields are aligned at 8-byte boundary - the compliler uses a different alignment strategy. For example, the size of

{$A8}
type
  TToTry = record
    a: byte;
    b: word;
    c: longword;
  end;

is 8 bytes in Delphi 2009, because the compiler aligns 2-byte value at 2-byte boundary, 4-byte value at 4-byte boundary, and the only actual alignment in the above example is b field aligned at 2-byte boundary.

As for the original question what changed between Delphi 7 and Delphi 2009 - read Sertac Akyuz answer and my comment to it

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文