zoukankan      html  css  js  c++  java
  • 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写的不知道代码有没有高亮,用了插件的)

  • 相关阅读:
    转:浅谈图片服务器的架构演进
    转:Spring AOP详解
    转:Spring AOP 注解方式实现的一些“坑”
    spring AOP自定义注解方式实现日志管理
    spring的普通类中如何取session和request对像
    spring session
    转:通过Spring Session实现新一代的Session管理
    转:一个Restful Api的访问控制方法(简单版)
    Python : 什么是*args和**kwargs[转载]
    tornado项目注意点
  • 原文地址:https://www.cnblogs.com/huangjacky/p/1619315.html
Copyright © 2011-2022 走看看