欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

指针---C语言的灵魂

程序员文章站 2022-04-16 10:19:35
很多朋友在初学C语言的时候,难免都会在指针这块停留许久,包括我本人。久久不能释怀,对其爱恨交织。静下心来,想总结一下自己在学指针的时候的一点心得,也是第一次写博客,激动万分,希望朋友们能多多指正、多多批评! 首先呢,针对指针,给初学指针的朋友一个最感性的认识:所谓指针,指的是,一个量,是一个储存的内 ......

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

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

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

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

指针---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类型变量的地址。

 指针---C语言的灵魂

  如果在指针变量声明之初确实不知道将此指针指向何处,最简单的方式是将其置为“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 }
指针---C语言的灵魂
 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”。

 

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

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