都说C语言最重要的是指针,其实本质上也就是对内存的理解。
那好,我们就从内存上开始谈。
首先,我们知道数据在内存中是以二进制进行存储的,二进制包括0和1。而为什么以二进制进行存储是因为机器只能识别电信号,而电信号只有正电和负电之分。
下面谈一下内存的存储单元问题
在内存中的最小存储单元是bit,也叫比特,比特位,位,都是一个意思。
而在一个bit中能够存储的内容就是一个0或者一个1。
8个比特位为一个字节,一个字节也叫做Byte。再往上就是KB,MB,GB。
1KB = 1024Byte 1MB = 1024KB 1GB =1024MB
不过这些大的存储单位我们不常用到,对于程序员最常用的是字节(Byte)和位(bit)。
------------------------------------------------------------------
上面对内存的存储单元 有所了解,接下来我们 来聊一下地址这个概念。
在程序中我们知道,每一个变量,每一个函数,每一个数组都有它的地址。但地址究竟是个什么东西?
我们知道 32位机器的地址是32位,64位机器地址是64位,其实这是由地址线的数量来决定的,而地址线是物理上的存在。那么地址的作用是什么?其实是我们可以通过这个地址来访问地址指向的这块空间(这里就有点指针的意思了,别急我们继续往下聊)。
ok,那这样计算的话32位机器我们可以分配多少个地址呢?是2^32个。(64位同理)
那假如我们为每一个bit位分配一个地址,则内存总大小为2^32bit,不过后来专家们觉得这个可使用空间有点小,于是最终决定为每一个字节分配一个地址,则32位机器可访问空间就变为了2^32Byte,一下子可用空间就变成了原来的8倍。
了解到这里我们已经知道,每一个字节空间都会有一个对应的地址。
----------------------------------------------------------------------
下面我们来说C语言的数据类型
举个例子:int 代表整型 char 代表字符型,这是最基本的理解,但其实更重要的是 这两种类型所代表的空间大小。int 代表4个字节,char代表1个字节。
我们联系到指针上来,继续举例:int a = 0x12345678; 这条语句的意思是,我们开辟了4个字节的空间用来存储0x12345678这个十六进制数据。0x12345678转换为32位二进制为00010010 00110100 01010110 01111000,到这里会涉及到一个大小端存储的问题,这里我不细讲,简言之以小端存储为例这个数据在内存中具体的存储顺序为01111000 01010110 00110100 00010010 ,每个字节翻译成十六进制就是 0x78 ,0x56,0x34,0x12。
现在,在a这个变量里面每一个字节存储的数据,我们都清楚了。下面我们通过指针来读取数据。
在此之前说一下指针的用法,举个例子:int * p ;(忽略野指针) 这条语句的意思是创建一个指针变量p,这个指针所指向空间的数据类型是int,注意:* 说明了p是一个指针类型,int说明的是p指针所指向空间的数据类型。
接下来我们将a的地址赋给p指针,p = &a; 我们知道如果想取得a的值可以通过解引用的方式,即*p ,这样我们就可以得到a这个变量的数据。此时我们知道p指针的类型为int* ,所以我们通过 * 这个解引用符,可以读取4个字节的空间,这个道理需要明白。那假如我们将 p指针的类型强制转换为 char* 又会发生什么呢?没错,我们可以一个字节一个字节的去读取数据。下面为演示代码
---------------------------------------------------------
下面为强制转换后,读取数据
这样我们就通过指针读出了第一个字节的数据,那我们可以不可以读第二个,第三个字节的数据呢?当然可以! 别忘了每一个字节都会有一个地址。
只要我们在p2这个指针变量上加1,就可以访问第二个字节的数据,第三第四个字节以此类推。下面是代码
---------------------------------------------------------------------
那么问题来了,如果我们用p指针能不能做到一个字节一个字节的访问?答案是不能,原因是p的指针类型为int* ,这意味着p指针每次加1,它都会跨越4个字节,如果用*(p+1)就会造成越界访问,因为我们不知道变量a后面的存储空间是什么状态。下面为代码演示
此时虽然没有报错,只能说变量a后面的空间存有数据,但这个数据明显不是我们想要的。如果后面的空间没有被使用,此时你解引用就会报错。
好了,文章暂时就写到这里啦,要说的东西太多,一下子说不完,哈哈
ps:如有不当之处,欢迎指正哦