1:内存和地址
1:内存
我们知道计算机上面的CPU在处理数据时,需要的数据是在内存中读取的,处理后的数据也会放回内存中,那我们买电脑时,电脑内存是8GB/16GB/32GB等,那这些内存空间如何高效管理呢?
其实也是内存划分为一个个的内存单元,每个内存的大小取1个字节。
一个bit(比特)位可以储存一个二进制的0或1;
一个Byte(字节)可以储存8个比特位。
然后往上是1kb=1024Byte,
MB,GB,TB,PB
2:地址
我们知道了内存的贮存方式,地址就是给一个个内存,取的一个编号,这些编号也叫做地址,也叫做指针。
内存单元的编号==地址==指针
3:正确理解编址
CPU访问内存中的某个字节空间,必须知道这个字节空间在内存的什么位置,⽽因为内存中字节很多,所以需要给内存进⾏编址。
计算机中的编址,并不是把每个字节的地址记录下来,⽽是通过硬件设计完成的。
钢琴、吉他 上⾯没有写上“剁、来、咪、发、唆、拉、西”这样的信息,但演奏者照样能够准确找到每⼀个琴弦每⼀个位置,这是为何?因为制造商已经在乐器硬件层⾯上设计好了,并且所有的演奏者都知道。本质是⼀种约定出来的共识!
⾸先,必须理解,计算机内是有很多的硬件单元,⽽硬件单元是要互相协同⼯作的。所谓的协同,⾄少相互之间要能够进⾏数据传递。
但是硬件与硬件之间是互相独⽴的,那么如何通信呢?答案很简单,⽤"线"连起来。
⽽CPU和内存之间也是有⼤量的数据交互的,所以,两者必须也⽤线连起来。
不过,我们今天关⼼⼀组线,叫做地址总线。
硬件编址也是如此
我们可以简单理解,32位机器有32根地址总线,每根线只有两态,表⽰0,1【电脉冲有⽆】,那么⼀根线,就能表⽰2种含义,2根线就能表⽰4种含义,依次类推。32根地址线,就能表⽰2^32种含义,每⼀种含义都代表⼀个地址。
地址信息被下达给内存,在内存上,就可以找到该地址对应的数据,将数据在通过数据总线传⼊CPU内寄存器
2:指针变量和地址
1:&取地址操作符
理解了内存和地址的关系,我们就需要知道,在C语言中创建变量,本质上是在向内存申请空间,比如:
如果我们使用printf函数把a的地址打印出来,则会打印出 0x006FFD70 这个最小的地址 ,&a取出来的是a所占4个字节中最小的字节的地址。
虽然整型变量占用四个字节,我们只需要知道第一个字节的地址,我们就可以推出后面a的地址。
2:指针变量和解引用操作符*
1:指针变量
如果我们通过&操作符拿到了一个数据的地址,我们如何存放这个数据的地址呢,那就可以将地址放入指针变量中。
比如
指针变量也是一种变量,这种变量是专门来存储地址的,存放在指针变量中的值,会被默认理解为地址
2:如何拆解指针类型
我们看到上述代码中pa的类型是int*,*是在说明pa是一个指针变量,而前面的int是在说明pa指向的是整型(int)类型的对象。
以此类推,char类型的变量,就存在char*类型的指针变量中。
3:解引用操作符
我们将地址保存起来后,我们应该如何使用它呢?
这个时候我们就需要引入解引用操作符*
如下
上⾯代码就使⽤了解引⽤操作符, *pa 的意思就是通过pa中存放的地址,找到指向的空间,
*pa其实就是a变量了;所以*pa = 0,这个操作符是把a改成了0.
有同学肯定在想,这⾥如果⽬的就是把a改成0的话,写成 a = 0; 不就完了,为啥⾮要使⽤指针呢?
其实这⾥是把a的修改交给了pa来操作,这样对a的修改,就多了⼀种的途径,写代码就会更加灵活,后期慢慢就能理解了。
3:指针变量的大小
前⾯的内容我们了解到,32位机器假设有32根地址总线,每根地址线出来的电信号转换成数字信号后 是1或者0,那我们把32根地址线产⽣的2进制序列当做⼀个地址,那么⼀个地址就是32个bit位,需要4 个字节才能存储。 如果指针变量是⽤来存放地址的,那么指针变的⼤⼩就得是4个字节的空间才可以。 同理64位机器,假设有64根地址线,⼀个地址就是64个⼆进制位组成的⼆进制序列,存储起来就需要 8个字节的空间,指针变量的大小就是8个字节。 注意: 1:32位平台下地址是32个bit位,指针变量⼤⼩是4个字节 2:64位平台下地址是64个bit位,指针变量⼤⼩是8个字节 3:注意指针变量的⼤⼩和类型是⽆关的,只要指针类型的变量,在相同的平台下,大小都是相同的。4:指针变量的意义
调试我们可以看到,代码1会将n的4个字节全部改为0,但是代码2只是将n的第⼀个字节改为0。 结论:指针的类型决定了,对指针解引用的时候有多⼤的权限(⼀次能操作几个字节)。 比如: char* 的指针解引⽤就只能访问⼀个字节,而 int* 的指针的解引用就能访问四个字节。 注意: 指针的类型决定了指针向前或者向后⾛⼀步有多大(距离)。5:void指针
在指针类型中有⼀种特殊的类型是 void * 类型的,可以理解为无具体类型的指针(或者叫泛型指 针),这种类型的指针可以⽤来接受任意类型地址。 但是也有局限性, void* 类型的指针不能直接进行指针的+-整数和解引用的运算。比如用于函数中。
3:const修饰变量
变量是可以修改的,如果把变量的地址交给⼀个指针变量,通过指针变量的也可以修改这个变量。 但是如果我们希望⼀个变量加上⼀些限制,不能被修改,怎么做呢?这就是const的作⽤。例如
上面代码中,m可被修改,n不可被修改。
但是如果在指针层面上那么n值就可以被修改。
4:const修饰指针变量
在上面const修饰变量中,可以从指针层面上可以修改变量。
但是我们也可以在指针层面上,将指针用const修改,这样避免了全部可以修改变量的方式。
直接说结论
1:const如果放在*的左边,修饰的是指针指向的内容,保证指针指向的内容不能通过指针来改变。 但是指针变量本⾝的内容可变。 2:const如果放在*的右边,修饰的是指针变量本⾝,保证了指针变量的内容不能修改,但是指针指 向的内容,可以通过指针改变。5:指针运算
指针运算有三类:
指针+- 整数 指针-指针 指针的关系运算6:野指针(野狗)
野指针是什么?
野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)1:野指针的成因
1:指针没初始化
指针没初始化,指针指向的空间就是未知的
2:指针越界访问
3:指针指向的空间释放
比如在主函数中用指针变量,储存子函数中某个数的地址。
2:如何避免野指针
1:指针初始化
2:避免指针越界
3:指针变量不再使⽤时,及时置NULL,指针使用之前检查有效性
4:避免返回局部变量的地址
当指针变量指向⼀块区域的时候, 比特就业课主页:https://m.cctalk.com/inst/s9yewhfr 我们可以通过指针访问该区域,后期不再使⽤这个指针访问空间的 时候,我们可以把该指针置为NULL。 因为约定俗成的⼀个规则就是:只要是NULL指针就不去访问, 同时使⽤指针之前可以判断指针是否为NULL。 我们可以把野指针想象成野狗,野狗放任不管是⾮常危险的,所以我们可以找⼀棵树把野狗拴来, 就相对安全了,给指针变量及时赋值为NULL,其实就类似把野狗栓起来,就是把野指针暂时管理起来。 不过野狗即使拴起来我们也要绕着⾛,不能去挑逗野狗,有点危险;对于指针也是,在使⽤之前,我 们也要判断是否为NULL,看看是不是被拴起来起来的野狗,如果是不能直接使⽤,如果不是我们再去使⽤。7:assert断言
转载一下
https://blog.csdn.net/weixin_45031801/article/details/136882008
标签:变量,32,地址,理解,深入,内存,指针,字节 From: https://blog.csdn.net/2403_87752060/article/details/143415448