C语言指针的本质详解-----初学者适用

C语言指针的本质详解-----初学者适用,第1张


一、概述
    指针是C语言最有力的工具。之所以说C语言是中级语言,能够和底层设备进行交互直接 *** 作硬件,就是因为C语言有指针,能够直接对硬件的内存地址进行访问,对其进行 *** 作,这是其他高级语言所不具备的,是C语言独有的强大功能。也正是因为指针能够直接访问地址,C语言的程序的大小和运行速率都是其它语言难以逾越的。所以对于指针的运用对于C语言来说显得尤为重要。
    初学指针时对于指针的概念总是会产生混淆,没有足够的理解,所以将学习过程进行了总结,也有了一些自己的理解,在此分享出来和大家共同学习,如果有不正确之处,欢迎大佬们批评指正,共同学习。

二、指针的概念
1.指针就是地址?
    在教科书还是各C语言的教学视频中经常会听到:“指针就是地址”这句话,但仔细思考一下觉得这样的描述是不准确的,或者说只对了一半。为什么这么说呢?下面我们来了解一下。

1、计算机数据存储方式:
    我们知道目前为止所有的计算机都是二进制计算机,数据在计算机当中是以二进制0和1来存储数据的,所有的数据,无论是字母、标点符号、字符还是数字在计算机中都是以二进制0和1来表示。其中分为数值型数据和非数值型数据,数值型数据就是我们所认识的数量的数;非数值型数据就是英文字母、标点符号还有键盘的按键之类的。其中数值型数据就是将我们日常生活中所应用的十进制数转换为二进制数在计算机当中存储;非数值型数据则由美国国家标准局 (ANSI)制定了一个标准规范叫ACSII码,将0---127的数字每一个对应上一个字符,从此以这当中的每一个数表示一个字符。

2、数据占用空间大小:
    现在我们知道计算机中是以二进制0和1来表示数据的了,但我们平常总会接触到内存大小的概念,因为在冯诺依曼体系架构的计算机中,内存对于计算机的性能是至关重要的,那么计算机中二进制数所占用内存大小我们该如何判断呢?
    1.bit:
    我们规定每一位二进制数所占用的内存大小为 1 bit,读作一位。
    2.byte:
    1 byte = 8 bit;    ----   一字节 = 8位;
    计算机当中一个字节是计算机数据存储的最小有效数据计量单位;几位数或者一字节零几位等等这样的数在计算机中是没有意义的,也就是说计算机数据的存储也是有原子性的,就好比组成元素的最小单位是原子,要么是一个原子,要么是两个原子等一整个的原子,不能说是1.3个原子这种带小数的,必须是一整个;这样的话我们就可以将内存条看成如下图所示;
    
 

3、地址:
    通俗地来说,地址就是数据在内存中存储的位置,这只是概念,可以帮助我们理解。但具体点,地址就是每个字节的存储位置编号,如上图所示。举个例子来说,将存储数据的内存条比作我们学校的宿舍楼,二进制数就是每一个宿舍里面的学生,每一位学生在这个宿舍楼当中所占大小为1bit,并且这个宿舍是个8人间,这8个人是一个团结的集体,并且老大到老八地位分明,,是一个整体。学校如果想对这个宿舍里的集体进行 *** 作,必须先找到这个宿舍,并且学校只能找到宿舍,找不到指定的学生,找到宿舍的方式有两种,一种是通过宿舍编号找,就好比计算机通过地址找内容;另一种是通过宿舍的名字,就好比通过变量名找内容。

4、地址编号:
    既然说地址就是每个字节的存储位置编号,那让人好奇的是这个编号是怎么编的,对于它所能表示的最大编号有没有限制?它的位数是多少?这就取决与 *** 作系统了。我们知道常见的 *** 作系统有32位和64位的,那什么样的是32位的 *** 作系统,什么样的64位的 *** 作系统?它取决于什么?
    简单来说,具体是多大的 *** 作系统,取决于计算机的CPU是几位的,能够并行处理(同时处理)几位的二进制数?那在CPU为64位的计算机上能不能安装使用32位的 *** 作系统呢?毫无疑问是可以的,反之却不行。
    对于32位的 *** 作系统,它能并行 *** 作32位的二进制数,也就是说它能并行 *** 作的的最大范围位是32位二进制数的,也就是 2^32bit = 4Gb;既然能 *** 作,那肯定是能够访问这些内存的地址的,计算机只有能够访问该地址,才能对其进行 *** 作。这些数的编号就是地址,也就是0000...0000---1111...1111,用16进制表示的话是0x00000000---0xffffffff(一个16进制数代表4个二进制数,那32位二进制数也就是8位16进制数)如上图所示。其能同时 *** 作的范围大小也就是4Gb,所以说对于一个进程,系统会开辟一个0--4Gb的内存空间。
    对于64位的 *** 作系统也是同样的原理。
    由此我们可以清楚地知道,对于一个 *** 作系统,每个数据的地址是固定的,是一个常量,并且地址的大小跟 *** 作系统的位数直接相关,32位的 *** 作系统的地址大小为32位,也就是4 byte,4个字节。同理,64位的 *** 作系统的地址大小为64位,也就是8 byte,8个字节。

总结: 
    1.地址是每一字节的位置编号,一个地址只对应一个字节,一个字节,一个字节!!!;
    2.地址是常量;
    3.地址占用内存大小:32位的 *** 作系统---4字节,64位---8字节。并且地址的大小也是常量!!!。
    现在我们清晰了解了地址的概念,这十分有助于理解接下来指针的概念。

2.指针不是地址!
    
1、指针的本质
    从本质上来说,指针是一种变量,它之所以叫指针是因为这个变量当中存储的内容是地址,所以这个变量就像有方向一样指向存储的地址内存储的变量,所以叫指针,更准确地说应该叫指针变量,指针存储的内容才是地址。并且指针存储的永远是  首地址!
2、指针的大小
    指针的大小取决于存储的内容大小,也就是地址的大小,32位的 *** 作系统,地址大小为4个字节,所以32位 *** 作系统的指针大小也为4个字节,并且永远是4个字节!
3、指针的类型
    既然指针是变量,那么在定义指针时就必须给指针声明其数据类型,想要搞懂指针的数据类型,就必须要搞明白数据类型究竟代表着什么含义。
    ---数据类型:
    之前我们在定义变量时总会给这个变量声明数据类型,比如空类型    void;基本类型的字符类型char、整型short、int、long、浮点型float、double;构造类型的 结构体类型struct、共用体类型union、枚举类型enum等等。那这些到底是什么含义呢?
    同样地,声明变量类型就是给赋予原子性,比如int型,它占四个字节,因为数据在内存条中按字节为最小单位存储,所以int a的意思就是说从内存条中申请连续的4个字节,把这4个字节当做一个整体,给这个连续的4字节的内存空间起名为 a。从此以后这四个字节连为一体变成32位,这32位的二进制数代表一个数据。所以说数据类型的含义就是给这个变量从内存条中申请多大的连续空间。
    ---指针数据类型
    同样地,假设 p为一个指针变量,我们定义变量p时定义为int a;int *p=&a; 按上述推断来说,指针的数据类型的含义也是从内存条中申请多大的内存空间,然后给这个空间起名为 p;但值得注意的是,指针p当中存储的是地址,而地址的大小是固定的,在32位 *** 作系统中地址的大小就是4个字节,所以从内存条中给指针p 申请的内存空间大小都应该为4个字节,而且无论指针p的数据类型是什么类型,从内存条中给变量p 开辟的内存空间都应该为4个字节;那既然这样,给指针变量p 声明类型还有什么意义呢?为什么还要给指针变量p 声明数据类型?然而再思考一下,指针变量p当中存储的是变量a的首地址,该变量中存储的是一个int型的4个字节的数据,但是变量p指向的首地址只存储变量a的最高字节的内容,这显然是不正确的。所以说我们必须要给指针变量声明该指针是指向什么类型的数据的,换句话说这个指针指向多大空间。现在的意思是指针变量p指向变量的首地址,数据类型告诉计算机这个指针从这个首地址开始指向多大的内存空间。那为什么要加*号呢?之前我们有学过,在地址面前加 *号的含义就是取该地址的内容,指针变量p中存储的就是地址,所以*p的含义就是取该地址的内容,再加上前面的数据类型,就是说该指针指向的变量是该类型的变量(指针p指向的内容为int型的变量)。
    从另一个角度讲,指针的数据类型指的是:指针现在已经指向了首地址,那么我们编程时必须需要清楚地告诉计算机这个指针到哪里结束,它映射多大的内存间?而且我们必须要保证指针变量的数据类型和指针所指向的变    量的数据类型是相同的,否则指针指向的数据将失去意义。
4、指针的意义        
    从上所知,在位数固定的 *** 作系统中,指针大小是固定的,在32位的 *** 作系统中,指针大小恒为4个字节。当指针指向的内容远比指针大的时候,只要使用指针指向其首地址,就可以对该数据集进行 *** 作,而不用在程序中一直开辟保存着该数据集占用进程的空间,可以动态的开辟空间,极大地降低程序的空间复杂度。
    并且由于指针是变量,所以可以对指针进行运算来访问不同的地址;
 


三、指针的使用
    对指针的概念有了深刻的理解之后我们简单总结一下关于指针的定义和使用。
1.基本类型指针的定义:
    1、一级指针
    定义格式:[存储类型]  数据类型 *指针变量名;
    例如:
        char ch=‘A’; char *sp=&ch;
              int a=10; int *p=&a;   
    2、二级指针
    定义格式:[存储类型]  数据类型  **指针变量名;
    概念:一级指针的指针;
2.数组指针
    1、 概念:指向数组的指针,本质是指针。(行地址)
    2.、 定义格式:
          [存储类型]  数据类型   (*数组指针变量名)[下标];
         下标:跨越的维度。
     int (*sp)[3];  //sp是数组指针  ,指向区域范围跨越了三个int型元素空间    (12byte)。
eg:一维数组
     int a[5]={1,2,3,4,5};
     int (*p)[5]=&a;  //*p--》&a[0]   *p+1-->&a[1]
3.指针数组
   1.概念:本质是数组,数组的元素是地址。
   2.格式:[存储类型]   数据类型 *指针数组名[下标];
        下标:元素个数  
   eg:int  a=10,b=15,c=20;   
        int *p[3]={&a,&b,&c};   //p[0] ==>&a 
4.函数指针
  1.定义:本质是指针,是指向函数的指针。
  2.定义格式:[存储类型]  数据类型  (*函数指针变量名)(形参列表);
eg:  int  (* p)(int ,int );//p可以指向返回值为int类型,有两个形参为int的函数。
4.指针函数
  1.定义:本质是函数,返回值是地址。
  2.定义格式:
       数据类型 *函数名(形参列表)
       {
函数体;
return 地址;//return NULL;
}
  解决返回地址空间被释放的问题;

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/1353131.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-14
下一篇 2022-06-14

发表评论

登录后才能评论

评论列表(0条)

保存