分享

指针

 向上人生路0f8b 2018-05-22

  很多朋友在初学C语言的时候,难免都会在这块停留许久,包括我本人。久久不能释怀,对其爱恨交织。静下心来,想总结一下自己在学指针的时候的一点心得,也是第一次写博客,激动万分,希望朋友们能多多指正、多多批评!

  首先呢,针对指针,给初学指针的朋友一个最感性的认识:所谓指针,指的是,一个量,是一个储存的内容是地址的一个量,这个概念包含两个点:

  一、指针是个量,对应着一块内存区域

  二、指针存储的信息是某个内存单元的地址。

 

  如上图所示,为了存储32位的地址数据,指针占据了4个字节,每个字节8个二进制位,该指针中存储的是某个类型在内存的地址数据,此处以double类型为例,double类型的数据占据8个内存字节,将其首字节的地址存储在指针中,这样,通过指针可以访问该double数据。这类似于现实生活中的地址和名片,习惯于把地址印在名片上,这里名片的作用和指针相同,存储的都是地址数据。

   很明显, 在引入指针的时候,多次提到了内存,那么很自然地,接下来先简单说一下内存的一些概念!

  关于内存

  大家都知道,内存是平时接触较多的一个概念,从硬件上说,内存是一个物理设备,从功能上讲,内存是一个数据仓库,程序在执行前都要装载到内存中才能被中央处理器(CPU)执行。

  1、计算机中的内存

  以Windows系统为例,执行安装在硬盘上的某个程序,实际上是将该程序的指令和数据读入内存,供CPU执行的过程。内存是由按顺序编号的一系列存储单元组成的,在内存中,每个存储单元都是唯一的地址,通过地址可以方便地在内存单元中存取信息。内存中的数据要靠电源维持,当计算机关机或者意外断电时,其中的所有数据就永远消失了。

  2、内存地址

  内存地址的引入是同样的道理,为了正确访问每个内存单元,要对其进行编址,以32位计算机为例,其地址空间为32位,采用32为地址编码,诸如0X87654321的形式。内存地址是连续的,相邻内存单元空间的地址差1,可以把内存看成一个平坦连续的一维空间。

  3、内存中保存的内容

  在计算机中,以二进制数据的形式存放的,每个内存单元的容量是1B,即8bit。内存是CPU唯一可以直接访问的大容量设备,使用Windows的朋友都清楚,双击某个可执行的程序,CPU会执行它,这实际上是复杂的内存载入过程:

    ① 程序所要进行操作的对应代码装载到代码区

    ② 全局和静态数据等装载到数据区

    ③ 开辟堆栈、供临时变量等使用

   4、内存与操作系统

   程序在运行前,需要向操作系统申请存储空间,在内存空间足够空闲的时候,操作系统将分配一段内存空间并将外存中软件拷贝一份到内存中,并启动软件运行,在运行期间,该软件分配的空间不再分配给其他软件,当软件运行结束后,将回收该软件内存空间(但并不清楚遗留数据,Java局部变量必须初始化,原因就在此,防止被之前的遗留数据影响)

  扯了这么多内存的内容,只是为了让初学的朋友有点感觉,也是为学习指针打一剂预防针!

  关于指针

  指针就是地址,地址就是指针。

  指针变量是存放内存单元地址的变量

  指针的本质是一个操作受限 的非负整数

   下面从两个方面来浅谈一下指针的问题:

  1、基本类型的指针

    指针的定义:类型 * 指针变量名;

    比如int * p; double * q;

复制代码
1 include 2 int main() { 3 int *p; 4 int i = 10; 5 int j; 6 // j = *p; 7 // p = &i; 8 // *p = i < -=""> i = i 9 // j = *p;10 // char ch = 'a';11 // p = &ch /*类型不一致 错*/12 // printf('i = %d,j = %d,*p = %d',i,j,*p);// 10 10 1013 return 0;14 }
复制代码

 在上面的代码3~5行定义了三个变量(如下图),分别是:int *形的变量p、int形的变量i、int形变量j。需要清楚的是在第三行中,p是变量名字,int * 表示该变量只能存储int类型变量的地址。

 

  如果在指针变量声明之初确实不知道将此指针指向何处,最简单的方式是将其置为“0”,C语言中提供了关键字NULL: int * pInt = NULL;

 如果放开第6行注释。会出现什么结果呢,势必会报错!因为*p保存的是地址,但这句话上面的代码中并没有给p赋值。p不知道哪个单元的,p指向的是一个不确定单元,垃圾数据!如果放开第9行就不会有这种结果了,因为在第7行对p进行了赋值,p变量保存了i的地址了!

 在第七行中,有三句话:

      ① p保存了i的地址值,p指向了i。

      ② 但并不说明p等于i或者说i等于p。修改i的值并不影响p的值,修改p的值也不影响i的值。

      ③ i能代表什么,*p就能代表什么。(如第8行)

再看第10~11行,这个赋值对吗?定义一个char类型字符,将他的地址保存在整形指针p中,毫无疑问,这也是错的,前面已经说了,int * 表示该变量只能存储int类型变量的地址。

接下来再看指针在函数中的作用:

复制代码
1 #include 2 3 void f(int i) 4 { 5 i = 100; 6 } 7 8 int main(void) 9 {10 int i = 5;11 f(i);12 printf('%d\n',i);13 return 0;14 }
复制代码
复制代码
View Code
复制代码

 在第一个代码中,如果想通过函数改变i的值,能成功吗?很明显是不行的,i是局部变量,i输出的最终值将仍是5。

 那么如何根据被调函数改变主调函数的值呢,第二段代码就实现了这个问题,通过使用”*指针”的形式,可以直接访问指针所指向的内存空间。换言之,因为通过参数传递,p保存了i的地址,那么*p 就等价于i,同时可以通过间接引用“*p = 100;”可以改写指针指向的区域!注意在第二段代码中的第二行,形参为 int * p ,不是定义一个名字叫*p的形参,而是定义了一个名字为p的形参,它的类型是int *!

 2、指针与数组的关系

  首先要明白,数组名就是一个指针,并且指向数组的首地址!数组元素的地址是连续的。

  数组名的含义:

    ① 是一个指针变量

    ② 存放的是一维数组第一个元素的地址

    ③ 值不能改变

    ④ 指向第一个元素

  下标和指针的关系:

    a[i] = *(a+i)

复制代码
1 #include 2 void printArray(int * p,int len) { 3 p[2] = -3; 4 /* p[0] = *p p[2] == *(p+2) == *(a+2) == a[2] 5 p[i]就是主函数的a[i] 6 */ 7 int i = 0; 8 for(i = 0; ii) { 9 printf('%d\n',p[i]);10 }11 }12 int main(void)13 { 14 int arr[5] = {1,2,3,4,5};15 printArray(arr,5);16 return 0 ;17 }
复制代码

  有了上面的理论再看这段代码,就不难理解,p[0] = * p;的含义了,因为数组名是一个指针,指向数组第一个元素的地址,所以数组的第一个元素p[0]就等同于*p了!

  因为'a[i] = *(a+i)',可以知道p[2] == *(p+2)因为p指向第一个元素,那么p+2就自然指向第三个元素了,所以p[2] = 3是给数组的第三个元素赋值,这事没问题的!但注意*(p+2)不等同于*p+2,后者是在*p指向的元素上,给这个元素的值加上2,前者并非如此,而是给他的地址“+2”。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多