**关于内存和指针操作,数据类型转换本质的理解

ZyxIp 2011-09-10 12:18:24
加精
Delphi 关于内存和指针操作,数据类型转换的本质的理解。
很多朋友问的问题感觉都是没有理解内存和指针与数据类型之间的关系。想解释一下。
很少写东西,觉得有些东西不好表达,就想到那说到那了,希望能提供一些帮助。
指针的使用,和使用指针直接读取数据是软件开发中经常使用到的技术,也是软件开发所需要掌握的基础,理解并能灵活的使用指针来操作内存,读写数据是软件开发必须要熟练掌握的基本。
内存可以看成是下图一个个的带颜色的小格子,每个小格子是一个字节的长度,<图一>共显示了8个小格式了,每个格子是一个字节,每个字节是由8bit 组成。

<图一>
因为我们一般操作的最小单元就是一个字节,所以展示内存布局的时候就用<图二>了。

<图二> 图上方的 1 2 ,3 …8 代表每个格子的地址。就象是房号一样。
内存的使用的是小单位是一个字节,Byte 类型和Char类型都是一个字节的长度。SizeOf(Byte)返回值是1,对计算机来说并不知道数据类型是 Byte还是Char ,它只是知道这个8个bit 的数据。
Var
B:Byte;
C:Char;
Begin
C:=’A’;
B:=65;
End;
这两上类型在内存中的保存都是二进制:01000001 ,当然因为我们能操作的最小是一个字节,即8个二进制长。01000001用十六进制表示为: $41。@B 表示得到数据B所在的内存位置,即数据所保存的房间号。如<图二>,@B将所返回 1 。

如果告诉你说这个字节保存的是 $41 ,你可能将它看成是 Byte 类型,也可以看成是 Char类型,这些数据类型只是人们自己为了操作方便而起的名字罢了。
Size(Word) 返回的长度是2 ,即两个字节。Var W:Word; 定义一个Word类型的变量,@W将返回的是第一个房间号,因为系统知道Word 是占用两个字节,所以在操作时候会将两个房间的数据来操作。如 @W返回的是 3 ,如<图三>

<图三>
则W的值是 17475 十六进制 $4443 。
Arr1:Array[0..1] of Byte; 一个占两个字节的数组,它的房号是 @Arr1 或者 @Arr1[0]。
如果 @Arr1 的值也是 3 则 Arr1[0]为$43 Arr1[1]为$44。
Arr2:Array[0..1] of Char ; 也是一个占两个字节的数组,如果 @Arr2 的值也是 3 则 Arr2[0]为C Arr2[1]为D。因为AScall码表中用 $43 即 67表示大字字母 C。用 $44 即 68表示大字字母 D。
以上说明,内存中保存的只是一些数字,至于这个数字是代表什么意思,是我们自己决定的,我们定义了很多的数据类型,如 Byte;Char;Word;Integer;Array [0..1] of Byte;Array [0..1] of Char;还有更多我们自己定义的结构。如
TMyInfo= packet record
ID:Byte;
年龄:Byte;
进球数:Word
End;

Var MyInfo: TMyInfo; @ MyInfo返回的地址是3 ,那ID=67,年龄=68岁,进球数=69个。
我们也可定义成如下这样的结构。说明每个 TMyInfo2结构占用SizeOf(TMyInfo2)=5 字节
TMyInfo2=packet record
进球数:Word
ID: Word;
年龄:Byte;
End;
Var MyInfo2: TMyInfo2; @ MyInfo2返回的地址是3 ,则内存中的数据表示的意思就成了,进球数=17475($4443) ,ID= 17989个($4645),年龄=71($47)岁
以上两个结构的内存布局是一样的,大小也是一样的,只是我们对它的解释不同,两个结构体就象是两个模具,对内存数据的意义根据模具的描述来确定。
上面两个结构也可以和下面的这个结构相同
TMyInfo2=Pack record
ID:Integer;
年龄:Byte;
End;
就上面的内存布局 ID =1178944579($46454443) 年龄=71($47)岁
也可以和下面的内存布局一样
Var Arr3:Array [0..5] Byte; Var Arr4:Array [0..5] Char;
Arr3 [0]=67;Arr3 [1]=68;Arr3 [2]=69;Arr3]=70;Arr3 [4]=71;
Arr4[0]=C;Arr4[1]=D;Arr4[2]=E;Arr4[3]=F;Arr4[4]=G;

我们时刻要明白我们定义的数据在内存中的真实布局情况是什么样子,我们对各种数据类型的转换,指针的移动就更明确了,我们可以将一个内存块的数据看成一个数组,也可以看成一个结构体,也可以看成是一个个的数字,在这些数据类型之间我们可以互相转换。也可以将一个结构体复制到一个数组中。ComyMemory 进行数据的复制是不管内存中的数据是什么业务意义的,只是将内存块进行复制罢了。

对内存操作,指针操作不理解的朋友都是没有明白这些操作,没有理解内存和数据类型的关系。理解了这些以后就可以更好的理解软件开发的过程,更好的解释很多的错误原因了。
...全文
2420 79 打赏 收藏 转发到动态 举报
写回复
用AI写文章
79 条回复
切换为时间正序
请发表友善的回复…
发表回复
浩南_哥 2011-09-20
  • 打赏
  • 举报
回复
再次进来学习
ZyxIp 2011-09-20
  • 打赏
  • 举报
回复
对内存,指针,线程这些的操作应用最典型集中常用的例子就是网络通信了。Socket收到的是数据流,至于数据的内容是什么根据双方的约定,比如都是以数据包的形式来保存一个类型或对象,数据包开头以定义的AAFF开始,然后是一个Integer来保存整个数据包的长度,然后是数据的内容,最后一位保存这个数据包的校验。解析的时候从收到的数据开始,先找 AAFF ,找后在在看数据的长度是多少,然后根据长度找到校验,这基本就可以认为是一个数据包了,然后在计算一下收到的数据的校验码看和数据包中保存的校验是否相同,如果相同就将这个数据包的交给对应的业务处理过程,否则就认为这个数据包是错误的,丢弃。
一般大家都用 TServerSocket 来做为服务器,TClientSocket 来做为客户端。对于TServerSocket来说,可以接收很多个客户端连接,每个客户端都会收发数据。对于每个连接上来的客户端来说都是独立的,要为每个客户端来创建一个接收缓冲区,因为有可能第一次只收到数据包的一部分,而且第二又收到了剩余的部分和每二个数据包的一部分。所以要通过缓冲区来保存。
缓冲区的大小可以先预设一个值,然后根据接收数据的大小和处理数据的速度来调整,如接收到100个字节,而这时缓冲区只有30个字节的位置了,那就将缓冲区扩大,我反正每次都将缓冲区扩大为原来长度的一倍在加上当前要接收的数据的长度。更安全点的话防止你的缓冲区无限的扩大,可以设置一个上限值,当达到这个上限时将缓冲区中前面旧的数据删除掉,一般来说如果缓冲区总是在扩大,说明你的数据处理太慢了。

如有100个客户端来连接了,那就是有100个缓冲区,为每个缓冲区设置一个临界区,每个缓冲区都是先进先出的环形缓冲区有Push ,Pop 两个方法,用临界区进行同步.
然后在创建一个线程,就是循环这0到99个缓冲区,检查缓冲区中是否有完整的数据包,如果有就处理,没有就检查下一个。如果你有1000个连接,一个业务处理线程可能有些少,那可以设置一个值,比如说每个线程就只处理300个连接,那第一个线程就只循环0..299,第二个只循环300到599,第三个只循环其它的。
通迅部分的效率优先级是最高的,不要阻塞,有些写法都是在 OnClientRead中去检查是否有完整的数据包,如果有话就直接调用了业务处理程序,并在界面上显示,这造成了通迅接收部分的执行时间太长。很容易出错和系统不稳定。就算是用了缓冲区,然后由另一个线程进行读缓冲区,也不要在线程中去处理复杂业务和直接去操作界面控件显示。因为你在缓冲区数据读取线程中执行太多的工作的话,如你在处理1号客户的数据,长时间的处理,这时在来了数据就不能及时读取,会造成缓冲区不停的扩大。
如果有完整的数据包,这个数据包的内容要显示在界面上,先最好将这个数据包转化成具体的业务对象,然后将业务对象保存一个列表中,然后给窗体发一个Postmessage消息,窗体收到消息后在从列表中读这个业务对象。或在窗体上放一个TTimer,定时的去列表中检查,看是否有业务对象,如果有的话就处理。
如果收到的数据包是要保存到数据库,那就提交给数据库管理对象。
如果收到的数据包是要保存成文件,那就先创建一个临时文件,将数据直接写入,然后通过消息的方式让界面显示。
总的来说:就是利用Postmessage,或TTimer在加上数据列表,将通信部分和业务部分完全的分离开。

通信部分的代码最起码不要写在 TForm窗体中,最好写和 TDataModule中。界面只是用来展示和接收输入的,一定要将它各业务处理,底层通信这些分离开,不能耦合。
trista_career 2011-09-19
  • 打赏
  • 举报
回复
恩 说的是啊~
王三的猫阿德 2011-09-19
  • 打赏
  • 举报
回复
这个好东西额。。指针是神器 啊。。
m60a1 2011-09-19
  • 打赏
  • 举报
回复
其实,为什么还是有太多的人没搞明白,也可能这辈子都搞不明白

值和地址,他们一直没搞明到底是什么意思,到底是什么区别

取值和取地址的区别到底是什么

为什么会发明值和地址

怪就怪操作系统吧

是它们抽象出来

如果只有汇编

世界就和平

又和谐了
ZyxIp 2011-09-19
  • 打赏
  • 举报
回复
Word类型保存是高位在前,低位在后,只要理解成计算机当初设计就设计成这个样子就行了。

其实当初设计成低位在后 高位在前 也可以,但是现在就是这个样子了。我们知道就OK了。

至于当初设计的真正原因,那可能得翻翻计算机设计原理一类的底层的书了。
scow 2011-09-19
  • 打赏
  • 举报
回复
嗯,说的是啊
zmkkobe 2011-09-19
  • 打赏
  • 举报
回复
不错,呵呵
feimashenhua 2011-09-19
  • 打赏
  • 举报
回复
顶顶顶顶顶顶顶顶顶顶顶顶顶顶顶顶顶顶顶顶顶顶顶顶顶顶顶顶顶顶顶顶顶顶顶顶顶顶顶顶顶顶顶顶顶顶顶v
ChampionHeart 2011-09-19
  • 打赏
  • 举报
回复
问下LZ,如下部分:--------
因为系统知道Word 是占用两个字节,所以在操作时候会将两个房间的数据来操作。如 @W返回的是 3 ,如<图三>

<图三>
则W的值是 17475 十六进制 $4443 。
-----------------------
W的值为什么不是十六进制 $4344呢?怎么知道是从高位开读还是从地位开始读的呢?

lijianbin9 2011-09-18
  • 打赏
  • 举报
回复
经典!!!
a8522816 2011-09-18
  • 打赏
  • 举报
回复
虽然看不懂,但是感觉楼主很厉害,顶了!!
xujianglun 2011-09-18
  • 打赏
  • 举报
回复
Delphi...
好久没有用了。。忘差不多了
jdgh220 2011-09-18
  • 打赏
  • 举报
回复
学习了~~~
perfecthcm123 2011-09-18
  • 打赏
  • 举报
回复
努力学习
jakey0108 2011-09-18
  • 打赏
  • 举报
回复
现在牛人真多 学习了
ZyxIp 2011-09-17
  • 打赏
  • 举报
回复
总共就20分,肯定是不够分得了。

指针和内存操作是软件开发的基础操作,一定要搞的明明白白才可以。
媒体盒子 2011-09-17
  • 打赏
  • 举报
回复
学习的路过
gaojiymxn 2011-09-17
  • 打赏
  • 举报
回复
回个有分吗
加载更多回复(54)

16,748

社区成员

发帖
与我相关
我的任务
社区描述
Delphi 语言基础/算法/系统设计
社区管理员
  • 语言基础/算法/系统设计社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧