有趣的指针

废话不多说,请看以下代码:

#define buffer ((char *) *( (int far *)0x200 ))
main(){

     buffer=(char *)malloc(20);
     buffer[10]=0;
     while(buffer[10]!=8){
          buffer[buffer[10]]='a' + buffer[10];
          buffer[10]++;
     }
     free(buffer);
}

 

我们不妨忽略#define buffer ...这个语句,把buffer看做b代码如下:

main(){

     b=(char *)malloc(20);
     b[10]=0;
     while(b[10]!=8){
          b[b[10]]='a' + b[10];
          b[10]++;
     }
     free(b);
}

b指向了刚刚开辟的20个字节大小的内存首地址,且b移动的单元为1个字节(即char类型单元大小)

b[10]=0意思是由b指向的首地址向后移动10*1(b的移动单元)个单位到达的地址,也就是第11个元素所在内存的首地址

根据n[10]=0;b[10]!=8和循环中的b[10]++;可知该循环循环8次

b[b[10]]='a'+b[10];这句什么意思?
循环第一次:b[0]='a'+0
循环第二次:b[1]='a'+1
循环第八次:b[7]='a'+7
我们枚举下,可知原来是把a~h八个字符分别装到b[0]~b[7]中

free(b);释放b所指向的地址,可能问题来了?free如何知道b所指向的空间有20B大小呢?

答:存放在b所指空间大小存放在寄存器ax中,并压入栈ss中保存。

(寄存器:cpu中可以储存数据的器件)

举个栗子附图:

我们写的c代码如下:

我们使用系统自带的debug调试一次这个程序


首先,已知main在我的虚拟机中的偏移地址为01fa,这样我们直接跳至cs:1fa处,

找到main函数中转化成汇编代码的c语言代码。

SUB SP,+02 (汇编指令)等价于 sp+=2; 想必大家已经猜出来了,这正是char *b;其含义就是初始化一个空间给b(姑且可以这样抽象的理解),而这个空间将放在栈(ss:sp注:括号中不理解没关系,ss<<2+sp代表的是栈的物理首地址)中,sp是指向栈顶的,所以其+2(为什么+2?因为b是一个指针,指针大小是2Byte,栈存储单位是1B,所以指针入栈栈顶需要+2,姑且可以这样抽象的理解)

之后,MOV AX,0014 (汇编指令)作用等价于AX=20;这里的0014是16进制,转化为10进制就是20,即我们malloc的空间大小。

PUSH AX (汇编指令)翻译过来就是ax的值入栈,把ax的值保存在栈中,就是保存malloc分配空间size的值。

于是乎,我们证明了free是如何知道malloc所分配的空间大小。ax=20;ax入栈;到运行free时在从栈中取出20即可。

简而言之,这个简化的程序就是把a~f8个字符写入申请的b[0]~b[7]中

 

那么,重点来了(注意!这里才是重点)

define buffer ((char *) *( (int far *)0x200 ))

这里第一反应就是:Are you kidding me?

你会说为啥变量变成一个数了呢?还是16进制的数。

答:首先,*( (int far *)0x200 )

far:指明后面的对象是一个确切的物理地址(就是根据0x200能直接在内存中找到是0x200的物理地址)

第一个* :代表后面的对象是一个内存空间

第二个* :代表后面的对象是一个内存空间地址

(int far *)0x200:表示这个一个物理地址,该地址的存储单位是2Byte(int即2Byte,但有些操作系统不同,这里不多做解释)

*(int far *)0x200:表示0x200这个物理地址的内存空间

(char *) *( (int far *)0x200 ):char * 纯属修饰的是内存空间中的数据,表示这个数据是一个char 型指针,(以下可以跳过)

本质是:这个数据是一个地址,一个单位为1Byte的空间的地址。也就是0x200地址空间中装着一个单位为1Byte的地址,这个地址我们还未确定,因为我们还未初始化。

 

b=(char *)malloc(20);初始化0x200地址内存,即把malloc开辟的首地址装入0x200中

b即装在0x200地址内存中的数据。

b[10]:装在0x200地址内存中的数据是一个地址,b[10]即这个地址+10Byte(char)地址内存中的数据。

这里来波实战:

首先,我们写一个c代码,这里的编译器用的是古老的TC,为什么要用这么老的东西?

因为越古老的东西越不存在那些华丽而又繁杂的装饰,资源分配,链接,预编译特别简单,便于我们分析,我们甚至可以自己动手写一个c语言编译器开发环境。

之后,我们运行完这个程序,查看0x200地址的内存,(图中0000:0200代表0x200地址)

发现内存数据为00 00 ,根据我们前面的推测,内存数据00 00 是一个地址,是malloc分配空间的首地址。

为什么只取前两个?

malloc此类的函数分配的空间都是在栈中,所以,对应的前两个字节应该是栈的偏移地址。

 

于是,我们来到地址00 00 处:

ds:0000可以抽象理解为查看0000地址处的内存。

这里前面是以16进制表示,后面表示的是16进制转化为ascll的值

我们可以看到a~h的ascll码十六进制和其转化为字符形式,从b~(b+7)地址内存中依次存储(这里b的地址即00 00)

于是,我们证明了0x200中存的数据是一个地址,且这个地址是malloc的首地址。

最后留一个小彩蛋,那么b[10]在哪呢?其中的值又是多少?仔细看看00 00地址内存的那张图,你将会得到答案!

 

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值