用汇编的眼光看C++(之指针1)

 【 声明:版权所有,欢迎转载,请勿用于商业用途。  联系信箱:feixiaoxing @163.com】

 

    指针是我们在C/C++中经常遇到的一种数据类型。指针用的好,可以提高代码的可读性;但是如果使用不恰当,反而会造成很大的麻烦。指针,也就是指向某一种数据类型的地址。这种类型很多,它可以是编程语言自带的类型,比如说int、long、short、char、float、double、int;也可是是指向某一种自定义数据类型,可以使union、struct或者是class;甚至指向的数据类型本身即是指针,比如说int*、char*、short**;当然指针还可以是指向一片内存,表示具有一定长度的起始地址,比如说int(*pData)[4];最后,指针还可以是函数指针,直接指向函数运行的第一个字节。

    (1)普通数据类型指针

    普通数据类型指针相对概念比较简单,它表示指向的区域就是普通数据类型的空间。我们可以看下面一段示例代码:


 
 
  1. 43: int m = 10;
  2. 004012F8 mov dword ptr [ebp- 4], 0Ah
  3. 44: char* p = ( char*) &m;
  4. 004012FF lea eax,[ebp- 4]
  5. 00401302 mov dword ptr [ebp- 8],eax
  6. 45: float* f = ( float*) &m;
  7. 00401305 lea ecx,[ebp- 4]
  8. 00401308 mov dword ptr [ebp- 0Ch],ecx
  9. 46: short* s = ( short*) &m;
  10. 0040130B lea edx,[ebp- 4]
  11. 0040130E mov dword ptr [ebp- 10h],edx
  12. 47: *p = 2;
  13. 00401311 mov eax,dword ptr [ebp- 8]
  14. 00401314 mov byte ptr [eax], 2
  15. 48: *f = 2.4f;
  16. 00401317 mov ecx,dword ptr [ebp- 0Ch]
  17. 0040131A mov dword ptr [ecx], 4019999Ah
  18. 49: *s = 10;
  19. 00401320 mov edx,dword ptr [ebp- 10h]
  20. 00401323 mov word ptr [edx],offset process+ 46h ( 00401326)

    上面的一段代码出现了四种数据类型,三种指针,我们可以一一梳理一下。m、p、f、s都是函数内部的临时变量,因为指针也是一种数据类型,它保存的数据不再是一种char或者是short、int数据,而是一种地址。所以我们对p、f、s进行复制的时候,都是把m的地址一一拷贝给他们的。所以虽然指针类型不同,实际上p、f、s的数值是一样的。下面对指针指向的空间进行数据赋值的时候,就和指针类型相关了。一般来说,如果指针为char类型,那么计算就局限在指针指向的那一个字节里面;如果指针是int类型,那么运算的范围就是指针指向的连续4个字节;当然如果指针是数据结构体或者是class类型,那么指针操作的内存区域就更大了。所以,我们返回到代码的时候发现,*p=2只是操作了一个byte,*f=2.4f的时候,操作的是四个byte、也就是dword,*s=10的时候,赋值的就是一个word,这里0x401326处其实就是数据0x00 0A。函数内的变量进过这一番折腾之后,m数值还是10吗?大家可以好好思考一下?(其实是0x4019000a)

    (2)函数类型指针

    下面是一段有趣的代码,可以查看函数的地址。


 
 
  1. void add()
  2. {
  3. printf( "hello!\n");
  4. }
  5. void process(int* q)
  6. {
  7. int* address = ( int*) add;
  8. __asm{
  9. call address
  10. }
  11. }

    这段代码使用了嵌入式汇编,但是理解上面没有什么困难,感兴趣的同学可以直接拷贝到VC上面进行编译,当然还要加上头文件和main函数。通过代码,我们可以发现其实address就是一个地址,call address其实和call add是一样的。

    (3)指针的指针

    指针的指针,其实就是说我们指针指向的数据类型本身就是指针。但是,总之一句,指针是地址,那么指向地址数据的指针本身也是地址。


 
 
  1. 46: int* pp = &p;
  2. 004012FF lea eax,[ebp- 4]
  3. 00401302 mov dword ptr [ebp- 8],eax
  4. 47: int** ppp = &pp;
  5. 00401305 lea ecx,[ebp- 8]
  6. 00401308 mov dword ptr [ebp-0Ch],ecx
  7. 48: int*** pppp = &ppp;
  8. 0040130B lea edx,[ebp-0Ch]
  9. 0040130E mov dword ptr [ebp-10h],edx
  10. 49: assert( sizeof(p) == 4);
  11. 50: assert( sizeof(pp) == 4);
  12. 51: assert( sizeof(ppp) == 4);
  13. 52: assert( sizeof(ppp) == 4);

    指针是一种保存地址的数据类型,那么指针的指针也是一种地址数据,保存的也是地址。以此类推,指针的指针的指针呢。。。。。。上面的代码已经清楚地说明了这一点。虽然pp、ppp、pppp自身的意义有所差别,但是他们保存的数据却很简单,也就是堆栈的地址。pppp->ppp->pp->p,大家如果看懂了这个关系,就不会觉得指针很复杂了。如果还是不甚了了,可以把指针内容和指针地址分别打印出来,就会发现他们的区别了。下面就是打印的结果:


 
 
  1. p = 0xa, &p = 0x12ff20
  2. pp = 0x12ff20, &pp = 0x12ff1C
  3. ppp = 0x12ff1C, &ppp = 0x12ff18
  4. pppp = 0x12ff18, &pppp = 0x12ff14


 

(待续)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值