1.指针和指针变量的理解
指针是地址,地址是数据,数据可以用空间来储存;指针变量则是一种变量,一种用来保存指针地址的变量。若是要区分这两种概念,首先要明确变量的两个属性:变量有两个属性,一个是左值(空间,存放内容的空间),另一个则是右值(数据,占具体内存的数据)。那么对于指针,既然是地址,那么则可理解为右值。但对于指针变量,它是变量,则存在两个属性,左值和右值。
结论:指针是地址。指针变量是变量,其内部可以保存指针,也可以作为地址使用。区别就在于两者发挥作用时的含义是否相同。
2.指针的存在意义
在解释之前,举个例子:如果学校领导要去学生公寓中找学生,若果没有宿舍的门牌号,那么领导只能按着楼层去一层一层的寻找目标宿舍,其过程十分繁琐。但是如果可以根据宿管阿姨手中的门牌号手册去寻找,其过程就十分简单了。所以类比到计算机中,指针可以理解为某处地址的“门牌号”,可以在CPU通过‘总线’寻址访问内存时起指引作用,从而提高CPU寻址效率。
其中每个内存字节空间,相当于一个学生宿舍,字节空间里能够放8个比特位,就好比同学住的8人间,每个人是一个比特位。每间宿舍都有的门牌号就等价于字节空间对应的地址,即该空间对应的指针。那再从计算机硬件的角度去分析这个问题,首先有如下问题:
- cpu在内存中寻址的基本单位是什么?
- 在32位机器下最多能识别的物理内存是多少?
首先CPU在内存中寻址的基本单位是字节。计算机只认识二进制,32位机器,则有32位的地址线,1根地址线用有无电信号来表示0 1,则32根地址线则可以同时传输32个比特位,所谓的地址种类(编址)体现在地址总线无电信号的排列总和上,即地址数目为2的32次方。则其最多识别的物理内存为地址数目乘以1bit,即为4GB大小。
这里可以明确其CPU访问的内存是非常巨大的。如果没有指针,可想CPU去内存中寻址只能遍访所有的内存,一次近乎4GB的数据访问,将会严重影响CPU的寻址效率。(拓展)那字节的地址是如何形成的?计算机有很多硬件单元,硬件单元之间是相互协同工作(至少进行数据传递),编址不是每次将字节的地址进行存储,由前面地址计算大小可知32位机器下的物理内存在4GB左右,可以想象,若是每次计算机进行这样的字节存储,消耗的内存以及运转效率是巨大的。所以,编址是通过硬件设计完成的,就是说厂商规定了编址的流程,就像钢琴键上的黑白键的布局,也是由相关钢琴制作商完成的。所以当地址信息被下达给内存,在内存内部即可有指向性地找到对应数据,进而将数据通过数据总线传入cpu寄存器中。
结论:指针存在可以提高CPU的寻址效率
3.指针的认知观念
C语言中有很多数据类型方便我们去定义变量,不同变量有不同地址,所以催生出不同的指针。但无论是什么样类型的指针,都避不开这三个问题:指针的类型是什么?指针指向的类型是什么?指针指向了哪里?这三个问题将帮助我们解决指针是什么的问题。
int p;//一个整型变量
int *p;//p与*结合,p是一个指向整型内容的指针
int p[3];//一个整型数组
int *p[3];//一个指针数组。p与*结合低于p与[]结合的优先度。"int p[3]"中存放的是整型指针
int (*p)[3];//一个数组指针。p是一个指向由整形数据组成的数组的指针
int **p;//一个二级指针。p是一个指针,其在指向整形内容的指针,构成二级指针。
int p(int);//一个函数,其返回值是一个整形数据。
int (*p)(int);//一个函数指针,p是一个指向整型函数且返回值是整型的指针。
后面将介绍一些指针的拓展问题:
4.指针的内存布局
有如下代码:
int a=10;
int *p=&a;
以下是该代码中其指针的内存布局:
在这里要注意的点有两个: <1>解引用指针找到的地址永远从最小地址开始算起 。<2>*p是一个指针变量:*是一个操作符,在*p表达式中,*p即解引用(使用的是p的右值概念):取出p中的地址,并访问该地址指向的内存单元(空间),是一种间接寻址。
结论:对指针进行解引用,就是指针指向的目标。(变脸的艺人虽能变化多张脸谱,但其艺人并没有变)
5.可以直接通过地址数据去对变量进行访问吗?
因为现阶段存在内存保护机制,比如栈随机化,或者金丝雀等等,所以现阶段直接通过地址去寻找变量不太可能。例如在目前的vs2013上,使用局部变量运行后,或者定义全局变量后修改代码后,其地址都会随机变化,从而保护代码的安全性。
6.int* p和*p的区别
先看一段代码int a=10; int*p=&a; 前者中是定义一个指针p,然后存入a的地址,指向的地址以int格式读取;后者则是对指针p进行解引用,把int型的数字10存放到p指针指向的地址上。(前者是指针变量中的左值空间概念,后者就是a变量值)
7.指针中的地址强制类型转换
<1>把字符串“1234”转化成int 1234的转换,后者中字符串的1234是1 2 3 4字符,若是要转化成整型值1234,是真正需要经过算法转换,其前后的1234一定会发生改变。
<2>强制类型转换,举个例子说明本质:好人or坏人?(好比我们对某个人的看法,因其不同的行为和别人对其的评价从而给其贴好人坏人标签,但是那个人还是那个人,并没有因我们的看法而发生改变)
结论:强制类型转换,改变的是对特定内容的看待方式,在C语言中,就是只改变其类型。下面举一道经典习题:
首先这里结构体的大小为20,在16进制转换下为0x14.第一个问题中指针加1等于加其所指类型的大小,所以第一个显示为00100014;第二个问题中由于强制类型转换,将p看成一个无符号整型,所以加1相当于问0x100000+1等于多少;第三个问题将p看成一个指针,加1相当于加一个整型大小,即为0x100000+4
8.多级指针(这里以二级指针分析)
指针变量是变量,是变量就有地址,又地址是数据,数据可以被别的变量保存,所以存储指针变量的变量就是多级指针的产生原因。对于二级指针,看下图:
那么可以类推像三级,四级指针等等,都是这样的指向关系。我们看一段代码:
int a = 10;
int*p = &a;
int**pp = &p;
p = 100;//修改原先p指向a的地址,改为指向100的地址
*p = 100;//对p解引用,就是让a充当左值,存储100的地址
pp = 100;//修改原先pp指向p的地址,改为指向100的地址
*pp = 100;//对*pp解引用,就是让p充当左值,存储100的地址
**pp = 100;//就是让a充当左值,存储100的地址
结论:在解决多级指针的问题时,牢记解引用就是指向指针所指的目标,指针变量可以充当左右值执行不同的功能。
以上就是我对指针的部分看法,若有不足之处,希望各位看官不惜赐教。