C指针系列一

  就语言本身而言,C/C++真心让人害怕!最近大致看了java和python,感觉用起来真的很方便,在具体的方向上的确让我省事不少,譬如做简单的推荐系统或数据挖掘上,即便如此,还是比较喜欢分析指针。由于水平有限,还不能很好的写几篇关于机器学习方面的博文,故这里先摆上重新对C指针的一些认识。水平太差,错误之处还望纠正

(1)指针的本质

  提到指针的概念,脑海就会自觉的浮现一些词:存放变量地址的变量,4字节(32位机器上而言),还有呢???其实,谈到理论概念,在编程语言中,不论是内置型还是自定义的数据类型都可以认为是ADT(抽象数据类型),即定义一些规则和一些操作,如int型就是一个范围限制在[-2^32, 2^32-1]的数据集合,还有限制的操作(加、减、乘、除等),同理指针也可以视为一种独特的数据类型,可以进行加、减等运算。理论说完了,接着利用反汇编去认识一下指针的实质。

  指针变量只是个存放地址的变量;那为何会有int、float型等指针变量呢?难道有专门标识指针类型的语句隐藏在对应的底层代码之中?(下面标"//"后的代码为反汇编代码)

1 int i;
2 void main()
3 {  
4    int *pi = &i;//标为语句1
5   // C7 05 C0 74 41 00 BC 74 41 00 mov    dword ptr [_pi(4174C0h)],offset _i (4174BCh)
6 }    
根据反汇编的代码来看,语句1简单的将&i(变量i的地址0x4174BC)存放到[0x4174C0]中(pi地址所标记的内存单元)----此时,我们就说pi的值为0x4174BC(i的地址)。目前,根本没有什么语句来标记pi是int型指针变量,即便在写一个语句(float *pf = &f)如此。好吧,继续探究!!!
  
  前面回答了一个问题,即根本没有任何语句去标识一个指针变量的类型;那所谓的指针不就是一个4个字节变量,用于存放地址。那C/C++中出现的int*、float*等是如何确定呢?指针的强制性转换又是怎么一回事呢?------这些问题得一一解答咯。
  的确,指针变量就是大小为4个字节,用于存放其它任何变量的地址;肯定在某个地方标识了指针类型,而我们却不知道而已!!!(注意下面代码中下划线标记的地方)
 1 int i;
 2 short s;
 3 int *pi;
 4 short *ps;
 5 int main()
 6 {
 7    pi = &i;
 8     //C7 05 74 71 41 00 68 71 41 00 mov         dword ptr [_pi (417174h)],offset _i (417168h)
 9    *pi = 12;
10     //A1 74 71 41 00   mov         eax,dword ptr [_pi (417174h)]
11    ps = &s;
12     //C7 05 64 71 41 00 42 71 41 00 mov         dword ptr [_ps (417164h)],offset _s (417142h)
13    *ps = 18;
14    /* B8 12 00 00 00     mov         eax,12h
15       8B 0D 64 71 41 0   mov         ecx,dword ptr [_ps (417164h)]
16       66 89 01           mov         word ptr [ecx],ax 
17   */
18 }

  原来,标识指针类型的语句并不存在,只有当其访问内存之时才“凸显”出来,dword(double word)--4字节,word--2字节。即利用一个int型指针去访存时,就可以读/写4字节,而short型指针访存时,只能读/写2字节,即指针变量本身的类型决定了它们访问内存时的字节数。那么强制性转换后会有什么变换呢???

(2)指针的强制性转换
  下面摆出了最简单的代码,便于分析指针强制性转换时发生了什么(这里没有将代码进行反汇编)
 1 int main()
 2 {
 3   int i = 0x00001234; 
 4   char *pc =(char*)&i;//char *pc = &i是不能通过编译的
 5   printf("i = %x\n",*pc);//请问这里应该输出多少呢?
 6    /*另一组测试代码
 7        short  j = 0x5678;
 8        int *pi =(int*)&j;
 9        *pi = 12;//会出错么?
10    */
11    return 0;        
12 }        

  这里假设是低位优先的平台下,即低字节的34存放在内存中最低位(由于不会插入表格,就不给出具体的内存结构图)。上面程序运行结果*pc=34(16进制),而下面的注释的程序运行会出错。

  对于指针强制性转换操作,做出如下总结:指针的强制性转换只不过是一个”欺骗“编译器的举措而已,能让我们的程序代码逃过编译器的法眼;但是通过编译阶段绝对不是我们能高枕无忧的理由。指针的强制性转换没有特殊的操作,只是简简单单的地址赋值而已,真正的作用在利用指针去访问内存时才显现出来。指针的本来类型决定指针访问内存的字节数(强制性转换后还是一样),pc只能访问1字节,即便是 &i 强制性转换赋值给它。ok...赋值后pc保存就是变量 i 的地址,*pc操作就是根据 i 的地址去访问指向的内存单元,但是pc的类型决定了它只能访问1个字节,故结果是34h。
  而下面的程序运行为何出错呢?pi是int型指针,故利用pi去读写内存时,其访问内存大小为4字节;现在将short型对象地址强制性赋值于pi,这里有错么?当然没有。。。至少编译器会让你通过。。。真正出问题的是*pi = 12,解引用操作是根据pi存放的地址(这里就是&j),然后找到对应内存单元。注意:此时根据pi的值我们找到了存放j的内存单元,然后。。。将12存放其中(原有的数据当然被覆盖了)。。。Oops...我是int型指针耶,我要访问的是4字节,我当然要填充0x0000000c进去咯。而 j 只有2字节,只好借用两个”未知“的字节补上(为什么称为"未知"呢?因为我们不知道那两字节存放的数据是垃圾还是宝贝),未知的行为是很危险的,执行时可能不能通过。
 
 

  

原文地址:https://www.cnblogs.com/Happyhe/p/3114559.html