指针的是是非非——初学入门必备宝典

    指针是大家c语言学习过程中的拦路虎,今天让我们一起揭开指针神秘的面纱,让指针不再神奇!

(-) .编址的理解

(--).指针的本质

    (---)  .指针变量

        [1] 空间和内容

        [2]指针变量

        [3]取地址

        [4]*p的理解

        [5]规避野指针

(-)理解编址

计算机内是有很多的硬件单元,而硬件单元是要互相协同工作的。所谓的协同,至少相互之间要能够进行数据通信传递。但是硬件与硬件之间是互相独立的,它们是通过“线”连接起来的。我们程序的执行是先将代码加载进内存中,然后通过CPU的读取和写入来实现程序的功能。在这过程中,CPU和内存之间是需要进行大量数据的交互,所以,两者必须也用线连

(1)地址总线: CPU访问内存中的某个字节空间,必须知道这个字节空间在内存的什么位置,而因为内存中字节很多,所以需要给内存进行编址。(就如同宿舍很多,需要给宿舍编号一样。计算机中的编址,并不是把每个字节的地址记录下来,而是通过硬件设计完成的。

32位机器有32根地址总线,每根线只有两态,表示0,1【电脉冲有无】,那么一根线,就能表示2种含义。地址信息被下达给内存,在内存内部,就可以找到该地址对应的数据,将数据在通过数据总线传入CPU内寄存器,实现数据的交互。因此,内存当中的编址并不需要开辟空间为其存储,而是通过硬件电路的方式对内存进行编制的,也就是说,我们内存的那些地址值,高地址到低地址的排序等这些都是硬件中规定好的,我们只需要遵守它的规定,正确使用规定就好。而我们所说的地址,又称之为"指针"。

(--).指针的理解

    地址的本质是数据,且地址具有指向性的作用。指针就是地址。指针又可以理解成“具有指向性的数字”。在VS2017集成开发环境中,通过按下键盘F10进入程序调试阶段,打开内存,就可看到地址数据,从内存中可看到,地址(内存单元)从上往下依次递增;地址的字节数为4个字节。linux平台下,地址(内存单元)从下往上依次递减

(---)指针变量

        (1)我们先讨论变量的空间和内容俩个概念

       第一个a做为变量来存放数据10,此时a做为空间来使用,代表变量属性;一般情况下,把使用空间的概念称之为左值

  第二个a做为数据保存进变量b中,此时的a取变量的内容来使用,代表数据属性;一般情况下,把使用内容的概念称之为右值。

结论:任何一个变量名,在不同的场合中,代表不同的含义!

    作为变量属性时,表示空间(可以存放数据的),一般格式表示为在赋值符号“=”的左边,称左值;

    作为数据属性时,表示内容,格式一般表示为在赋值符号“=”的右边,称右值。

    (2)而指针就是地址,指针变量是用来存指针的,指针是唯一标识一块地址空间的

    宿舍楼相当于计算中的内存(计算机中所有程序的运行都是在内存上进行的。所以为了有效的使用内存,就把内存划分成一个个小的内存单元,每个内存单元的大小是1个字节。;访问内存的基本单元是字节,而一个字节,由8个比特位组成),所以一个字节相当于一间宿舍,宿舍中的每一张床位相当于一个比特位

     我作为包工头叫李四去宿舍楼找到张三的具体位置,需要这栋宿舍楼的楼牌号和门牌号;对应的,CPU要在内存中寻址时,如果没有给内存单元进行标记,那么CPU要找到内存单元位置,效率是很低的。因此,为了提高效率,我们给内存单元进行了编号,这些编号被称为内存单元的地址,地址就叫做指针。

     每个地址标识一个字节(内存单元),那么我们就可以给出(2^32 Byte <–> 2^32 / 1024 KB <–>2^32 / 1024 / 1024 MB <–> 2^32 / 1024 / 1024 / 1024 GB <–> 4GB)4G的空间进行编址。在32位的机器上,地址是32个0或者1组成二进制序列,那地址就得用4个字节的空间来存储,所以一个指针变量的大小就应该是4个字节。那如果在64位机器上,如果有64个地址线,那一个指针变量的大小是8个字节,才能存放一个地址。指针的大小在32位平台是4个字节,在64位平台是8个字节。可知内存的最小单元为一个字节。

  (3) &a的地址值是变量a在内存中开辟4个字节数的最低地址(起始地址)。如何访问这4个字节,是通过找到该字节的起始地址,然后根据类型连续访问4个字节。结论:对变量取地址,不用考虑该变量的类型,该地址是变量在内存中开辟众多字节数的最低地址。指针永远指向变量在内存中开辟众多字节数的最低地址。

 (4)  *p完整理解是,取出p中的地址,访问该地址指向的内存单元。(空间或者内容)(其实通过指针变量访问,本质是一种间接寻址的方式)*结论:在同类型情况下,对指针解引用,代表指针所指向的目标。即对指针变量p解引用,使用的是指针变量p里面的内容,即右值。

注意:int *p = NULL和*p = NULL的区别

 把p的值变成0x00000000

  

 然而下图呢:

  

        第一行:定义一个变量a并初始化,值为10;

  第二行:定义一个指针变量p,其指向的内存里面保存的是int类型的数据;在定义变量p的同时把p的值设置为a的地址;

  第三行:给*p赋值为NULL,即给p指向的内存赋值为NULL;而p本身的值,即保存变量a的地址并没有改变。NULL是一个宏,被宏定义为0

让我们一起来尽情玩耍代码吧~~

 第一行:定义一个指针变量p,其指向的内存保存的是int类型的数据,在定义变量p的同时把p的值设置为NULL(0x00000000第二行:p是一个变量,既然是变量,就得在内存中开辟空间,只要有空间,那么p就一定有地址,其地址值就是&p,其类型为二级指针int**,通过强制类型转换成一级指针int*,保存进指针变量p中,其不再指向NULL,而是指向&p;换句话说,就是定义一个指针,自己指向自己第三行:对p解引用,包含两层意思,第一种理解,解引用的操作本质是访问p的右值,相当于p的内容,而p的内容放的就是p的地址,换言之,就是把10直接放到变量p里面;第二种理解,对指针解引用,代表指针所指向的目标,当前的p指向的就是变量p(即自身),所以解引用后就是变量p;第四行:p是一个单独的变量,此时它有左值和右值,这里充当左值,相当于把20放到变量p的空间里面,此时表示指针变量p指向0x00000014地址对应的内存空间。

(5)指针运算

【1】指针+-整数

   指针变量的类型主要取决于该指针变量里面保存的指针对其解引用指向的目标是什么类型,该指针变量就是那种类型。

   我们对指针变量进行+1操作时,其本质就是将指针变量保存的指针往高地址进行移动,移动的距离(步长)取决于该指针变量是什么类型,如果该指针变量是整型指针变量,+1操作时,往高地址移动整型个字节数,即移动4个字节。将指针变量保存的指针往低地址进行移动,移动的距离(步长)取决于该指针变量是什么类型,如果该指针变量是整型指针变量, -1操作时,往低地址移动整型个字节数,即移动4个字节。

   指针变量的类型决定了指针变量(做为右值,取其内容)±n(整数)时,向低地址或者向高地址走n步有多大(距离)

【2】指针-指针

存在两个指针指向同一数组,两指针进行相减操作,结果为两指针包含的数组元素的个数。(总字节数除以每个数组元素所占的字节数)

int strleng(char*str)
 {
   char*start=str;
   char*end=str;
   while(*end!=0)
   {
     end++:
    }
  return end-start;

[5]如何规避野指针

 野指针的出现,会使我们我的程序发生错误,规避野指针成为我们不可忽略的问题。规避野指针的方式有以下几点:

(1)指针初始化;

 定义指针变量时,如果没有给其附上对应的地址,应该先给指针变量附上空指针NULL。

2小心指针越界;

3指针指向的空间释放后立即置为NULL;

 动态开辟内存空间,使用完毕释放空间后,要将指针置为空指针NULL,否则,这个指针就变成了野指针。

4指针使用前检查有效性。

 使用函数时,如果传入的参数有指针时,应该要在函数的语句块开头检查指针的有效性,如果指针为一个空指针,应当立即退出函数

误区总结:

 (1)当使用scanf("%d",a)时由于32位机的整数和地址传进去一样大,故程序不会报错。

  (2)常用套路是让函数返回特殊的不属于有效范围的值(常用0和—1)

  (3)数组变量本身是指针,本身表达地址,无需&,但数组单元要用&;

  (4)*p++:取出p地址的数据后,完事后把p移入下一位

  (5)不同类型的指针不可以相互赋值

  (6)void*指不知道指向什么东西,计算时与char*相同

  

  

猜你喜欢

转载自blog.csdn.net/m0_63203388/article/details/121216888