Delphi - 数据的理解

技术交流,DH解说.
哈哈,学一下xiaoy.
今天这里我想说一下,Delphi里面的数据,当然不是数据类型,如果有朋友想看数据类型的直接打开Delphi的帮助就可以了.我们主要来看下数据在内存中的分布,以及我们怎么使用,其实这就不局限于Delphi了.

任何数据在内存中都是一堆二进制,各种数据结构都是对这些二进制进行堆积木而已.比如说Byte,Char,Boolean只是使用一个字节的数据,而Word,ShortInt就使用2个连续字节的数据,以此类推吧.

基本数据是这样了,那么复杂一点儿数据呢?
首先看数组:

var
  A:array[0..7] of Byte;
  I:Integer;
begin
  For I:=0 to 7 do
    A[I]:=I * 3;
  ShowMessage(IntToStr(A[I]));
end;

我们可以看到代码运行后A中数据是0,3,6,9,12,15,18,21.
我们跑到Delphi里面去看一下:
image 
当然这里是16进制,自己转一下了.
我们结论了,数组中元素在内存中式紧挨着的.
不信?改成Integer看下

var
  A:array[0..7] of Integer;
  I:Integer;
begin
  For I:=0 to 7 do
    A[I]:=I * 3;
  ShowMessage(IntToStr(A[I]));
end;

image
注意:多字节的,如Integer在内存中都是小头的.也就是假如一个整数Integer是$01234567那么他在内存中应该是67 45 23 01的.嘿嘿,上面我们能看到这个8个Integer都是紧凑在一起的.

接着讲讲结构体.
我们必须要了解到什么叫做数位对齐,在C++里面也有这个概念.
在我们32位操作系统中,数据处理都是4个字节4个字节来的,以前有人在问计算机访问什么数据最快,一群人在这里说字节.瀑布汗.
有了这个概念我们可以知道,结构体每个域都想凑成4的整数倍.
看个例子:

type
  TA = record
    C:Byte;
    D:Integer;
  end;
  TB = packed record
    C:Byte;
    D:Integer;
  end;
var
 A:TA;
 B:TB;
begin
  A.C:=13;
  A.D:=255;
  B.C:=13;
  B.D:=255;
  ShowMessageFmt('%D,%D',[SizeOf(A),SizeOf(B)]);
end;

这里我们看到A的大小是8,B的大小是5.看下内存中的情况:
A的情况:image 0D后面填充了3个00
B的情况:image 0D和FF紧挨着的.
packed主要是为了节约空间但是牺牲了效率.
结论:结构体中的数据也是紧挨着的.

我这里不讲字符串了,以为原来博客有个文章讲了的.晚上回去转过来.

说了这么多有什么用?这个是大家最关心的.

1 数据类型的转换.
前几天看见GraphicEx里面一个东西:
TChunk = array[0..3] of Char;
然后他要把这个转换成一个Cardinal大家说怎么办?
肯定多数人想到的第一个方法是:

Type
  TChunk = Array[ 0..3 ] Of Char ;

Function Change( A: TChunk ): Cardinal ;
Begin
  Result := ( Ord( A[ 3 ] ) Shl 24 ) Or
            ( Ord( A[ 2 ] ) Shl 16 ) Or
            ( Ord( A[ 1 ] ) Shl 8 ) Or
            Ord( A[ 0 ] )
End ;

的确这样做没错.但是我们知道这个数组4个字节在内存的排序和Cardinal在内存中的排序是一样的,那么我们只要把它强制转换一下,当然这里不是用强制类型转换,是指针.

Function Change1( A: TChunk ): Cardinal ;
Begin
  Result := PCardinal( @A[ 0 ] )^
End ;

是不是简单多了?为什么可以这样?
我们来分析下:
A在内存中肯定是这样的: A[0] A[1] A[2] A[3]
一个Cardinal $01234567在内存中是: 67 45 23 01
那么我们只要让一个Cardinal从A[0]开始就行了,所以我们看到直接将一个PCardinal指针=A[0]的地址就行了.
OK,同志们觉得有更简单的方法没有?上面的代码是不是已经很精简了?
难道一句代码都不用就可以转换?
答案是可以的.

Function Change2( A: TChunk ): Cardinal ;
Asm

End ;

这样就可以了.
这里需要一点儿汇编知识,我先不讲了.原理一样的就是同一地址不同指针而已.

2 成块操作
当我们有两个结构数组,我们想Copy其中一个到另一个去,怎么办?
你又想用For循环?何苦何必呢?

procedure MyCopy(Count:Integer;var Dst;const src);
begin
  Move(src,dst,Count * SizeOf(TA));
end;

我们只需要调用
MyCopy(5,A[1],B[0]);
那么B[0]..B[4]的内容都到A[1]..A[5]里面去了.(A和B都是TA的数组)
为什么?
因为结构体内容在内存是连续的,所以我们只要指定好起始位置和长度,然后把这块内存copy到另一个地方去,不就实现了复制了么?

能力有限,今天就讲到这里,我是DH.
(LiveWriter写的不知道代码有没有高亮,用了插件的)

posted @ 2009-12-08 12:45  HuangJacky  阅读(1168)  评论(9编辑  收藏  举报
AdminLogin