深入理解并打败C语言难关之一————指针(1)

前言:

  已经好久没有写文章,最近的生活比较忙,我到现在为止已经学完了C语言的大部分内容了,所以我决定每天出一篇文章来复习我学过的知识,今天先来讲指针的相关知识,行了我也不多废话了,现在开始进入正文

目录:

1.内存与地址(这部分可以选择性的观看)

1.1 .内存

1.2 如何理解编址(这一部分我不太懂,所以我就从网上搜索一下的)

2.指针变量和地址以及指针变量类型的意义

2.1取地址操作符(&)

2.2指针变量和解引用操作符(*)

2.3指针变量的大小

2.4指针的类型

2.5void * 指针

3.1const修饰变量

3.2const修饰指针变量

4.指针进行的运算

4.1指针+-整数

4.2指针减指针

4.3指针之间的关系运算

正文:

1.内存与地址

1.1内存

   我们可以引入一个案例来解释一下内存是什么:假如你有一个朋友叫做小明,有一天你住进了一个酒店,你想要邀请小明来酒店来找你玩耍,如果你不告诉小明你的房牌号的话,他只能从一楼开始一个房间一个房间的来找你,这样会显的很麻烦,如果你直接告诉他你的门牌号的话,他会直接找到你人在哪里。我们把这个故事的内容类比到计算机当中去,这个酒店我们称之为一个内存,酒店里有很多的房间,我们把一个房间称之为一个内存单元,每一个内粗单元都有一个相对应的编号,我们把内存单元的编号称之为地址,地址也叫做指针,所以,内存单元的编号 == 地址 == 指针,所以这就是指针的实际意义,他就是一个地址。

1.2.如何理解编址(这部分内容其实我也不太明白,所以我从网上找点资料来进行完善了)

  

  将这个之前我先从这放一张图来更好的理解编址,CPU访问内存的某个字节空间的时候,必须要知道这个字节空间在内存的什么位置,并且因为内存中字节很多,所以需要给内存进行址(就比如酒店的房间很多,需要给房间编号一样),计算机中的编址,并不是把每一个字节的地址记录下来,而是通过硬件设计完成的。钢琴,吉他上面没有写到“剁、来、咪、发、唆、拉、西”这样的信息,但演奏者照常能够精确知道。本质上是一种约定出来的共识

⾸先,必须理解,计算机内是有很多的硬件单 元,而硬件单元是要互相协同⼯作的。所谓的协 同,至少相互之间要能够进行数据传递。但是硬件与硬件之间是互相独⽴的,那么如何通信呢?答案很简单,用"线"连起来。不过,我们这节课关心的线是地址总线:硬件编址也是如此 我们可以简单理解,32位机器有32根地址总线, 每根线只有两态,表示0,1【电脉冲有⽆】,那么 ⼀根线,就能表示2种含义,2根线就能表⽰4种含 义,依次类推。32根地址线,就能表⽰2^32种含义,每⼀种含义都代表⼀个地址。 地址信息被下达给内存,在内存上,就可以找到 该地址对应的数据,将数据在通过数据总线传传入 CPU内寄存器。
2.指针变量和地址以及指针变量类型的意义
2.1取地址操作符(&)
  对于如何知道内存,取地址操作符可以找到变量什么的所在的地址,下面直接进行演示,对于取到地址的演示  (对了地址的占位符是%p)
#include<stdio.h>
int main()
{
   int a = 12;
   printf("%p",&a);   //注意地址的占位符是%p
   return 0
}

  其实我们每次申请变量的时候就申请了一部分的内存空间,通过前面的了解可以知道 ,每个内存单元都有着编号,所以变量是有地址的,并且因为int是有四个字节空间的,所以每个字节空间对应着一个地址,而取出来的地址其实是最小的地址,不过我们已经知道最小的地址了,顺藤摸瓜就可以找到其余字节的地址了。

2.2指针变量和解引用操作符(*)

  2.2.1指针变量

  通过取地址操作符可以取到任意变量的地址,这些地址是需要存储起来的从而为了后期的使用,我们在初始化一个变量时,会把一个数存在这个变量里,由此可以类推,我们可以把存放指针的变量称之为指针变量,下面是如何进行创建:

#include<stdio.h>
int main()
{
    int a = 12;
    int *p = &a;   //这就是指针变量如何进行创建
}

对于指针变量,我们可以这样进行理解:以上面的式子举例,p是指针变量的名字,*指的是定义的是指针变量,int指的是地址所对应的类型,这个要记住,以后会有着很多指针类型等着我们去记。

2.2.2解引用操作符(*)

  可能很多人会有疑问,我们把一个变量取出地址了并且放在了指针变量里面了,我们未来想要使用这个数的时候要怎么用呢,其实,在C语言中,我们是可以通过地址来改变变量的数的,具体的用法我写在了下面,毕竟取出了地址啥也不可以干那还不如不多废这功夫 

#include<stdio.h>
int main()
{
    int a = 12;
    int * p = &a;
    *p = 13;
    return 0;
}

  这样可以把a数值里面的值进行改正 ,* 变量名  =  你想要的值,这就是如何进行解引用操作,可能很多人会想,如果想要把变量的值进行改变,直接 a = 13 不就好了,理论上这么干是没有问题的,但是我们在这可以比喻一下子,我相信各位都接触过《狂飙》这一部胡=剧,在这里面,高启强每一次想杀人的时候都不是靠自己出手,我们可以把它比做成变量,他一般会对老默说:老默,我想吃鱼了,把这种肮脏活交给老莫去做,我们在这里可以把老莫比做成指针变量,很多事情不用老大(变量)去干,让小弟(指针变量)操作就好了。

2.3指针变量的大小

  可能会多人会想指针变量的大小会和变量大小是相关联的,对于所以我们先卖个关子,先展示对各个变量地址的打印

 

  不难看出,这几个变量的地址居然是一样的,很多人对此疑惑了,为什么地址大小是一摸一样的,我也不卖关子了,我直接告诉原因,指针变量的地址和所在的运行环境有关,就比如我这个是x64的运行环境,所以有64个比特位,换算下来刚好是8个字节的长度,所以一共占8个字节,对于原因我也不解释了(这个其实是我直接记下来的,与上面的编址有关)。

2.4.指针的类型

  指针有很多的类型,他的类型和对于的常量是息息相关的,比如上面的int * ,除了这个,还有char * , float * , double *等等,这一些都是根据所对应地址的类型所决定的

2.5.void*指针

  这个指针在后期会有重大的作用,在指针类型中有⼀种特殊的类型是 void * 类型的,可以理解为⽆具体类型的指针(或者叫泛型指 针),这种类型的指针可以⽤来接受任意类型地址。但是也有局限性, void* 类型的指针不能直接进行指针的+-整数和解引用的运算。对于它,后面模拟某类函数的时候会出现的

3.const来修饰指针

3.1const修饰常量

  我直接明了了,被const修饰的常量,是不能在改变它的数值的,对于例子,可以看见下面的例子, 

  

  这个红线代表着,a是不可以被修改的,因为它已经被const修饰,const是代表着语法进行了限制,简单来说就是不可以被修改,不过我们可以绕过它来进行修改,这时候指针的功能就来了,如下图所示

  就拿我上面《狂飙》的例子一样,大哥不好动手,那么小弟动手就好了,这样的会造成一定的风险,就比如我们就是不想让变量的内容改变,但是这么做的话就会有风险,所以我们更是要对指针进行const的处理,那么现在进入下一节:

3.2.const修饰指针

3.2.1const在(类型)* 的左侧

  对于指针使用const要分成两种情况进行讨论,先来讲述下左侧的情况,不废话直接说功能:const在(类型)*的左侧的话,会让解引用指针变量的时候,不能在解引用了(这是用大白话来讲),如果采用官方话来说:const如果放在*的左边,修饰的是指针指向的内容,保证指针指向的内容不能通过指针来改变。 但是指针变量本⾝的内容可变,通过这一段的介绍可以看出,const在左边可以阻止改变内容去,但不能组织地址的改变,下面将会讲述如何限制地址的改变

3.2.2const在(类型) * 的右侧

  const在(类型) * 的右侧的话,会让其在改变所指向地址的时候,无法进行改变(这是通过大白话进行讲述),如果用严肃的口气说的话:const如果放在*的右边,修饰的是指针变量本⾝,保证了指针变量的内容不能修改,但是指针指 向的内容,可以通过指针改变。所以从这里就可以看出二者的不同,所以如果合理使用const的话,可以避免很多意外情况的诞生

4.指针进行的运算

4.1 指针+-整数

  这一个可以通过数组的案例进行解释,因为数组在内存中是连续存放的,所以只要知道一个元素的地址,就可以找到后面的,不过再说这个之前,先来讲一下这个的逻辑,对于指针+-整数,其实说白了就是类似+1的时候会多加一个类型(此类型指的是指针的类型)的地址,就比如int * 如果+1的话,会多加4个字节的地址,类似这样,下面我们直接进入对于数组如何通过地址打印对应元素:

  通过控制台的数据可以看出二者是完全一样的,对于上面式子的解释,可以这么认为 ,在上面的式子,p代表着数组第一个元素的地址(小小的剧透下,数组名也是数组中第一个元素的地址),在printf的引用中,p + i代表着arr[i]所在的地址,解引用后便就是arr[i]这个具体的数了,所以可以通过这个特性来对于数组进行打印(当然不能仅仅这么肤浅这个用法),对于更高级的请关注我后续的文章(不是我卖关子,很多内容后期学完了才可以听懂)

 4.2指减指针

  指针减指针的结果是指针与指针之间元素的个数,当然,这个计算是由前提的,它的前提是两个指针指向的是同一个空间,不然二者就没意义了,下面来展示它的用法,这里可以采用对于srlen函数的模拟来实现,我们知道,strlen函数统计的是\0之前的字符个数,通过它可以看出指针减指针的妙用:

  可以看出两者的数值是一样的,从这里可以看出指针减去指针的时候记录的数是两个指针相差的元素个数,因为p代表指针最后所在的位置,它减去刚开始的位置代表着二者相差的字节个数,所以从这里可以看出指针-指针带来的效果,以后这个也会多讲,对于很多函数的模拟会用到这个。

4.3指针之间的运算关系 

他们之间的大小关系会是通过地址的大小来确定的,把它类比成变量之间比大小就好,指针和指针之间的运算关系和这个是一样的,可以通过例子来看看怎么比较(这部分没啥好讲的)

#include <stdio.h>
int main()
{
 int arr[10] = {1,2,3,4,5,6,7,8,9,10};
 int *p = &arr[0];
 int sz = sizeof(arr)/sizeof(arr[0]);
 while(p<arr+sz) //指针的⼤⼩⽐较
 {
 printf("%d ", *p);
 p++;
 }
 return 0; }

结尾:

  今天本来想着写很多大,但是今天2晚上聚餐导致脑子有点昏,所以这篇文章就先写到这里了,预告一下,下一篇 将会讲到野指针,assert断言等重要知识,如果文章出现了错误,恳请您在评论区指出,希望大家多多点赞,我们下一篇见!                                                         

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值