指针---C语言的灵魂

2018-06-18 03:50:54来源:未知 阅读 ()

新老客户大回馈,云服务器低至5折

  很多朋友在初学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<stdio.h>
 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 10
13     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<stdio.h>
 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 }
 1 #include<stdio.h>
 2 void f(int * p) // 不是定义一个名字叫*p的形参,而是定义了一个p的形参,它的类型是int 
 3 {
 4     *p = 100;
 5 }
 6 
 7 int main(void)
 8 {
 9     int i = 5;
10     f(&i);
11     printf("%d\n",i);
12     return 0;
13 }
View Code

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

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

 2、指针与数组的关系

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

  数组名的含义:

    ① 是一个指针变量

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

    ③ 值不能改变

    ④ 指向第一个元素

  下标和指针的关系:

    a[i] = *(a+i)

 1 #include<stdio.h>
 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; i<len; ++i) {
 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”。

 

  今天就先到这里,指针的内容多多,日后还应继续总结,继续和大家讨论!

  初次写博,纰漏多多!望朋友们不吝赐教!

  

 

标签:

版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有

上一篇:C语言版数据结构及算法_快速排序

下一篇:传递指针参数(函数内部给指针赋值)示例