C语言学习记录230626

        今天倒是有不少有趣的事情,下午忙里偷闲看了一个B站的视频,里面讲的主要是一个在富士康工作十年后逃离富士康的本科老大哥,这位老哥倒是颇有种看透红尘的感觉了,虽然赚的不多,但其实更像是他不想太累,只想活的轻松点。给几个截图,倒是很有味道。

        怎样?是不是有种金句频出的感觉,看了这视频,我倒是没那么急了。仔细想想,确实没必要对生活抱有太多期待,生活嘛,过得去就行。

        废话不多说,开始今天的总结,今天主要总结的是一些基本的指针知识,下面简单说一下。

        (一)指针概念

                首先我们要知道指针到底是什么,我们通俗口头上说的指针,其实指的是指针变量,而正儿八经的指针其实指的是地址,那什么是地址呢?地址指的是内存里每个内存单元的所在位置,内存里每个内存单元都有一个地址,这个地址能便于我们对对应的内存区域进行各种各样的操作,一个内存单元是一个字节(byte)。每个内存单元的地址就是指针,指针就是地址,而存放这个地址的变量,我们称之为指针变量,也就是说我们通俗口头上常说的指针。

                我们都知道各个整数类型、浮点类型都有其各自在内存中所占的大小,通常因实现而有所不同,字符类型的变量所占空间都是一个字节,那么指针变量所占的内存空间是多少呢?这个就跟虚拟地址有所关系,如果虚拟地址是32位,那么意味着内存的地址有32位组成,也就是4个字节,如果虚拟地址是64位的,那么就是8个字节,我们知道了一个指针变量的位数以后,我们就能知道理论上对应位数的地址所能控制的最大内存单元数量,32位二进制数能表示4GB个内存地址,而64位则是2的32次乘以4GB,因此不难想象,64位的内存地址是一个很夸张的数量,以我们当前的内存大小来说,远远达不到它的理论控制极限。

        (二)指针类型

                我们定义一个指针的方式,是在对应类型变量的变量名前面加*号。

int *p,a;
int* p,a;

                新人最容易搞错的一点就是以为类型名后面加*表示定义对应类型的指针,实际上应该是加在变量名前,才表示他是指针,看到上面例子,实际上都是只有变量p是指向int的指针变量,而a是int类型的变量 。

                有的人可能会说,为什么需要在指针变量前面特意注明其指针指向的空间是什么类型呢?指针变量有没有必要给他说明清楚这个指针指向的是哪个类型的空间呢?当然,实际上这很有必要。

                此前我们尝试过直接对指针进行整数的加减,假如有一个int类型的数组a[5]={1,2,3,4,5},我们定义一个指针p=a,我们知道p指向的是第一个元素1,当我们队指针p+1操作后,*(p+1)的结果是2,我们知道p是地址,地址是以十六进制保存的,那么讲道理加1应该是只移动一个内存单元的空间,但实际上,我们对这个指向int类型的指针变量进行加一操作后,它在内存空间内直接跳过了一个int类型所占的内存空间。再举个例子,有一个char类型的数组a[5]={‘a’,‘b’,‘c’,‘d’,‘e’},同样定义一个p,这个p指向第一个元素a,当我们队这个p进行加一操作后,我们会发现这个指针指向的不是e,而是b,他不像int类型的指针变量一样跳过4个字节,所以我们可以知道,指针变量的指针类型,能用于控制指针进行整数加减时的步幅大小,一个指针变量进行整数加减时,实际上相当于:指针变量+整数=指针变量+整数*sizeof(指针所指的空间类型)。

                另外,假如有一个int类型的数组a[5]={1,2,3,4,5},定义指向int类型空间的一个指针变量p等于a,当我们采用间接寻址运算符时,&p的值等于1,假如我们这个指针变量p是真向一个char类型的空间时,我们会发现&p取得的数值出现乱码,如果从内存角度看,会发现这个&p仅仅只读取了元素1开头的第一个字节里的内容,这说明指针变量的类型决定了指针变量访问内存空间的权限大小,比如char类型的指针变量指针访问1个字节的内存单元,而int类型起码能访问4个字节的内存单元。

                综上所述,指针变量的类型决定了两个事情,一个决定了这个指针变量访问内存时的权限大小,另一个就是决定了指针变量进行整数加减时,他的步幅大小。

        (三)野指针

                什么是野指针?说的通俗一点,就是指向未知区域的指针,那什么情况下会指向未知区域呢?我简单来说一下。

                第一种情况是没有初始化就直接访问,这种情况是最容易理解的,定义了一个指针变量后,没有用任何初始化器对这个指针变量进行初始化,这会导致这个指针变量指向一个未知的区域,如果对这个指针变量使用了间接寻址运算符,可能会产生未知的错误。

                第二种情况是越界,这种情况最容易出现在指针表示的是某个数组的地址时,当利用这个指针变量访问了超过这个数组的内存单元时,由于并未取得那些内存单元的访问权限,所以会导致“非法访问”,这种情况也表示为野指针。

                第三种情况是当指针变量指向了某个函数的局部变量,我们知道函数的内部变量,他是自动存储期,即进入这个函数时生成,离开这个函数时消亡,当我们在调用这个函数时,用指针变量指向了某个内部变量并带出这个函数后,由于离开了这个函数,这个函数里对应那个内部变量在内存空间内已经被释放了,所以这时利用间接寻址运算符去找这个变量时也属于非法的。

                最后简单说一下规避野指针的几个方法,一个是注意初始化指针变量,由于空指针NULL,也就是地址为0的内存单元是不允许被访问的,所以当我们初始化变量为NULL之后,任何对这个指针变量的调用都是非法的,这样可以让我们锁定错误。同时针对上面第二种情况,我们在使用数组指针时,要特别注意越界问题。第三种情况下,我们要避免对某个内部变量进行取地址。

最后还有一种方式就是对指针变量进行检查。

        (四)指针运算

                指针变量之前我们也知道可以进行整数运算,这里就不多说了。其实指针变量还可以进行指针间的关系运算乃至于指针间的减运算。

                我们首先来说一下指针间的减运算吧,指针都是地址,他们之间的加减是基于什么原理呢?其实基于的原理就是,内存空间内地址是连续的,而数组的排列顺序也是连续的,他们同内存是一致的。那么也就不难想出一种应用方法,也就是利用一个数组末尾的指针减去一个数组开头的指针,从而得到这个数组的元素个数,实际上是这么个公式:

                同一个数组两个指针间的元素个数=末尾指针-开头的指针

                那有的人可能会问了,指针是地址,哪怕是做减法,那得到的应该也是字节数量,为什么直接变成了元素个数,这个问题其实跟前面的指针变量类型意义是一个问题,因为定义了指针变量所指向的空间类型,并且编译器制定了编译的规则,所以我们最后两个指针相减,得到的直接就是两个指针间的元素个数。

                那么指针间的关系运算又是怎么一回事呢?指针间的关系运算本质上也是大小运算,地址是由小到大排列的,那我们就可以利用这个特性对指针进行比较。但是有一点要注意根据国际规范规定,两个指针作比较,最多只能跟这个数组末尾后一位的地址比较,这个数组始端前一位的内存空间地址是不可以比较的。当然,实际上大部分的编译器也能编译,考虑到可能存在的移植性问题,建议以后所有的指针还是尽量不要去跟数组前一个内存单元的地址去比较。

        (五)二级指针

                所谓二级指针,也就是指向指针的指针变量。那么三级指针自然就是指,指向指针的指针的指针。怎样听起来是不是有种俄罗斯套娃的感觉?这种二级指针乃至三级指针怎么表示呢?实际上只需要在定义一个指针变量时,在这个指针变量前再多加一个*就行了。比如定义一个指向int类型空间指针的指针,我们可以写成int **a,解读的时候我建议可以从外往里解读,最外层的*表示,这是一个指向int的指针。往里一层的*表示这是一个指向外面那个*的指针。

                采用间接寻址运算符时,则是反向解析,假如int a=10,**b=&&a;,那我们可以利用间接寻址运算符写成**b,从而访问变量a。

                另外,指针的数组,是数组,数组里存放的是指针。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值