关于记录的长度sizeof()问题,求解!(200分)

S

skter

Unregistered / Unconfirmed
GUEST, unregistred user!
以下是本人在一个程序中定义的记录类型,另一位网友因为使用其它语言对其做二次开发,
因此他采用了读取长度的方式,不料读出来却不正确。本人也对以下记录结构产生的长度
变化不得其解。因为根据每种类型的长度来计算,自定义记录的总长度应该是175,但实
测其长度为184. 以下是本人在去信中一个摘抄,望网友们看后帮助分析一下:

==============================================================================
[green]不过,我信中曾谈到的类型转换,也是有根据的,因为在定义比如布尔类型时,因为
delphi将boolean等同于ByteBool,即长度为1个byte,而非WordBool(2byte),
LongBool(4byte).而对INTEGER,在delphi则被视同于signed int (=32位),即为4个byte.
因此我估计你说的长度转换不成功,可能会在这些地方有差别。 而对字符串,
一个在C中定义为20个BYTE的字串符,在delphi中,其长度实际上为21,因为第一个[0]
被用来存储数组长度了,所以RecStr的sizeof()是21,ConnectStr的sizeof()是51。
这次你谈的这个问题,估计也与这个有关。

因此,按以上的delphi对类型长度的定义,则TFicFmt的长度应该是以下相加而得:

S_Fdate: Tdate
= 8
S_FTime: Ttime
=8
S_NFTime: Ttime
=8
E_Fdate: Tdate
=8
E_FTime: Ttime
=8
E_NFtime: Ttime
=8
OnNet: boolean
=1
Howlong: Recstr
=21
connect: ConnectStr
=51
readdata: longint
=4
writedata: longint
=4
speed: integer
=4
serverID: RecStr
=21
localID: RecStr
=21 //最后总的长度应该是 175 byte.

但是,你提出的文件长度除以记录数,则正确的长度应该是184,而非175,
你这个说法并没有错。我也是百思不得其解,后来干脆做了一个test,计算了一下
sizeOf(TFicFmt),结果令我觉得奇怪,它的长度的确是184,而非175,那么说来,
可能是delphi对记录类型有某个莫名的原因而导致记录长度出现变异。
以下是我做这个测试的源码,窗体中放了2个元件,即一个按钮和一个Memo,
运行一下即可知:

/////////////////////////////////////////////////////////////

procedure TForm1.Button1Click(Sender: TObject);
type
RecStr = string[20];
ConnectStr = string[50];

TFicFmt = record
S_Fdate: Tdate
[blue] //上网起始日期[/blue]
S_FTime: Ttime
[blue]//电话费的起始时间[/blue]
S_NFTime: Ttime
[blue]//网络费的起始时间[/blue]
E_Fdate: Tdate
[blue]//上网结束日期[/blue]
E_FTime: Ttime
[blue]//电话费的终止时间[/blue]
E_NFtime: Ttime
[blue]//网络费的终止时间[/blue]
OnNet: boolean
[blue]//是否为上网记录[/blue]
Howlong: Recstr
[blue]//通话的时长[/blue]
connect: ConnectStr
[blue]//帐号[/blue]
readdata: longint
[blue]//本次读取的数据[/blue]
writedata: longint
[blue]//本次发送的数据[/blue]
speed: integer
[blue]//本次连网的速度值[/blue]
serverID: RecStr
[blue]//接入服务器IP[/blue]
localID: RecStr
[blue]//本地IP[/blue]
end;

var rec:TFicFmt;a,b:integer;
begin
memo1.Lines.Add(inttostr(sizeof(rec.S_Fdate)));
memo1.Lines.Add(inttostr(sizeof(rec.S_Ftime)));
memo1.Lines.Add(inttostr(sizeof(rec.S_NFtime)));
memo1.Lines.Add(inttostr(sizeof(rec.E_Fdate)));
memo1.Lines.Add(inttostr(sizeof(rec.E_Ftime)));
memo1.Lines.Add(inttostr(sizeof(rec.E_NFtime)));
memo1.Lines.Add(inttostr(sizeof(rec.Onnet)));
memo1.Lines.Add(inttostr(sizeof(rec.HowLong)));
memo1.Lines.Add(inttostr(sizeof(rec.Connect)));
memo1.Lines.Add(inttostr(sizeof(rec.readdata)));
memo1.Lines.Add(inttostr(sizeof(rec.writedata)));
memo1.Lines.Add(inttostr(sizeof(rec.speed)));
memo1.Lines.Add(inttostr(sizeof(rec.serverID)));
memo1.Lines.Add(inttostr(sizeof(rec.localID)));

a:=sizeof(rec.S_Fdate)+sizeof(rec.S_Ftime)+
sizeof(rec.S_NFtime)+sizeof(rec.E_Fdate)+
sizeof(rec.E_Ftime)+sizeof(rec.E_NFtime)+
sizeof(rec.Onnet)+sizeof(rec.HowLong)+
sizeof(rec.Connect)+sizeof(rec.readdata)+
sizeof(rec.writedata)+sizeof(rec.speed)+
sizeof(rec.serverID)+sizeof(rec.localID);

b:=sizeof(rec);

memo1.Lines.Add(inttostr(a));
memo1.Lines.Add(inttostr(b));
memo1.Lines.Add('');
memo1.Lines.Add(inttostr(b-a))
end;
////////////////////////////////////////////////////////////////////

这个情况,记录总长度为何与其组成元素的长度不一致,很古怪[/green]

以上问题,大家来帮助本人分析一下。谢谢!
 
为什么不用Packed Record,用Packed Record然后用sizeof()取长度就不会出错了
 
你就按一个记录,sizeof(record)读出来就行了,然后利用记录访问
主要你都用的是tdate,ttime之类的可能他们长了,如果你全用字符替代他们就可以了
 
to ugvanxk:
tdatetime=double,它的长度是不变的,为8byte,出现变化主要是在加入字符串类型时。
 
用Packed Record,是紧缩格式的。应该不会出错。
 
应该是对许齐的事, 比如
TNoUse1 = Record TNoUse = Packed Record
FData1:char
FData1:char

FData2:Integer
FData2:Integer;
end
end;
sizeof(TNoUse1)=4 sizeof(TNoUse)=3

Delphi的编译器会将变量在内存中的地址对齐, 而使用Packed关键字就不会对齐了,
引进Packed 关键字也是为了其它语言
 
to 一只没有缺点的狼:
是的,用紧缩没有问题,但是为什么不用,就会出现上面的情况?
 
感谢大家的帮助。
lb_icesea79,有道理,但对非紧缩类型,有解决的办法吗?
 
似乎没有更好的办法。还是用packet吧。其他语言如C,一样有这样的问题的。
 
{
00 00 00 00 00 00 00 00
00 00 00 00 00 00 F0 3F
00 00 00 00 00 00 00 40
00 00 00 00 00 00 08 40
00 00 00 00 00 00 10 40
00 00 00 00 00 00 14 40//前面6个浮点数每个占8个字节.

//这里OnNet占1个字节.它和后面的Howlong和起来占22个字节.
//Howlong长度占1个字节.___14即十进制的20
01 14 30 30 30 30 30 30
30 30 30 30 30 30 30 30
30 30 30 30 30 30 01 31
//二十个字符0后根着的是一个01是指只有一个有效字符.即31,为字符'1'
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 0A 00 00 00 readdata占后四个字节
//一直到上一行的0A之前共53个字节是connect的.它本来应该是51个,
//结果为了后面readdata字段起始位置是4的倍数,所以占了54个字符.
//也就是说,OnNet和Howlong和connect这三个长度不是4的倍数而且
//连续的字段合起来如果还不是4的倍数,则会在三个字段中的最后的
//一个字段后面补00 直到是4的倍数,这里是补了三个字节,由此可见,
//假如Howlong字段或者connect字段中某一个减少一个字段,readdata就
//可以前移四位,至止为止就可以减少四个字节.
14 00 00 00 00 00 00 00 //writedata和speed和起来占8个字节.
01 20 00 00 00 00 00 00 //serverID字段的开始,如果没有localID和serverID,这个记录就到此结束.
00 00 00 00 00 00 00 00
00 00 00 00 00 01 41 00 //让我们想想,如果没有localID字段,这行的01 41 00应该是00 00 00
//到上行01之前是serverID,01和后面的41(A)一直到结束共27个字节.所以localID占了27个字节.
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 //这里的serverID和localID两个字段的和前面三个字段一样,
//只不过这里是记录的结束,本记录的标准类型的字段的最长的是双精度浮点数占8个字节,
//整个字段会对齐成8的倍数,所以这里就加了6个字节. 假如前面因为Howlong字段或者connect字段
//少一个字节而减少4个字节,最后就只需补两个字节而节省最后的8个字节,导至最后总长度为176个字节.
//
}


如果用同样的一种语言进行读写,因为对齐方式是一样的,所以可以读出来,但是不同的语言之间如果要
读出来,就不能用记录或者结构来读,而必须自己编程处理对齐规则.
 
感谢大家的参与。特别感谢aizb,lb_icesea79.
 
顶部