一、内存和地址
1.内存
●在计算机的组成结构中,有一个很重要的部分,就是存储器。存储器是用来存储程序和数据的部件,对于计算机来说,有了存储器,才有记忆功能,才能保证正常工作。存储器的种类很多,按其用途可分为主存储器和辅助存储器,主存储器又称内存储器(简称内存,港台称之为记忆体)。
●内存又称主存,是CPU(中央处理器)能直接寻址的存储空间,由半导体器件制成。内存的特点是存取内存速率快。
●内存就是暂时存储程序以及数据的地方。
举一个例子:
假设有一栋宿舍楼,你的同学就住在这栋宿舍楼里面,如果有一天你想去找他玩,但这栋楼的房间都没有编号,你的同学也不好描述他所在的宿舍位置,你就只能挨个房间去找。
如果这栋宿舍楼有100个房间,最坏的情况,你可能会找100次房间,才会找到你的同学。但是如果我们给宿舍楼里的每层每间房都编一个号,比如三层二号房302,六层十四号房614。这样你的同学提前告诉你他在614号房间,你就可以很快高效的找到你的同学。
计算机中内存也是像宿舍楼这样,将内存划分为一个个的内存单元,每个内存单元的大小取1个字节。计算机中常见内存单位的换算:
每个内存单元就像一间学生宿舍一样,1个字节空间里有8个比特位,就好比一间宿舍住八个人一样。一个比特位上存储的是二进制的0或者1。
2.地址
每个内存单元也都有一个编号(这个编号就相当于宿舍房间的门牌号),有了这个内存单元的编号,CPU就可以快速找到一个内存空间。
生活中我们把门牌号也叫地址,在计算机中我们把内存单元的编号也称为地址。C语言中给地址起了新的名字叫:指针。由上图,每个字节都对应着一个编号,也就是地址。
所以我们可以理解为:
内存单元的编号==地址==指针。
关于计算机中地址的深入学习,请点击下面的链接阅读。
https://blog.csdn.net/m0_62183318/article/details/137638845
二、认识指针
1.取地址
理解了内存和地址的关系,在C语言中创建变量其实就是向内存申请空间,例如:
#include<stdio.h>
int main()
{
int n = 10;
return 0;
}
上述的代码就是创建了整型变量n,向内存申请了4个字节的空间,用于存放整数10,其中每个字节都有地址,上图中4个字节的地址分别是:
1 0x00D3F710
2 0x00D3F711
3 0x00D3F712
4 0x00D3F713
上面是如何得到变量n的地址的呢?这里要用到一个取地址操作符:&。
#include<stdio.h>
int main()
{
int n = 10;
&n;//取出n的地址
printf("%p\n", &n);
return 0;
}
这里是小端字节序存储,也就是这个整型变量的低位字节排放在内存的低地址端,后面会讲到这个知识点的。
由上面的代码,对变量n取地址,然后打印,会得到这个变量的地址为0x00D3F710(开头的0x表示十六进制),由此可知&n取出的是变量n所占4个字节中地址较小的字节的地址。虽然整型变量占4个字节,但是只要知道了第一个字节的地址,后面三个字节地址也就可以知道了,这样就能访问4个字节的数据了。
2.指针和指针变量
(1) 通过取地址操作符(&)拿到的地址是一个数值,比如上面n的地址00D3F710这个数值有时候也是需要被存储起来的,方便后期再使用,我们把这样的地址值存放在指针变量中。
#include<stdio.h>
int main()
{
int n = 10;
int* pn = &n;//取出n的地址并存储到指针变量pn中
return 0;
}
只要记住,用来存放地址的变量就叫指针变量。通常我们叙述时会把指针变量简称为指针,但实际他们的含义不同:指针就是地址。但是我们单独说指针变量时,他是一个变量,其中存放的是其他变量的地址,他是可变的。通过上面的代码来说明:
●1.指针— —即地址
比如上面的&n就是变量n的指针,一个变量的地址称为该变量的指针。通过变量的指针能够找到该变量
&n:n的地址或者叫n的指针
●2.指针变量— —专门用于存储其他变量地址的变量
比如上面的指针变量pn的值就是变量n的地址,指针与指针变量的区别,就是变量值与变量的区别
假设上面得到n的地址为0x0118F704,则pn=0x0118F704
(2) 指针的类型
指针也是有类型的,那要如何理解指针的类型呢?就比如上面的int* pn = &n:
int n = 10;
int* pn = &n;
指针变量pn的左边是int*,其中*号是在说明pn是一个指针变量,前面的int是在说明pn指向的是整型(int)类型的对象。
假设这里得到n的地址为0x0118F704:
所以这里pn的类型是int*,pn存储了n的地址,我们就说pn指向了n。上图中指针变量pn也是有自己的地址的,只是这里没有给出,后面讲二级指针时会说明的。
类似的,如果要存放一个字符类型变量的地址:
char ch = 'q';
char* pc = &ch;
指针变量前的*号不管是挨着类型名、挨着变量名或是单独空出来都没有影响,都是一样的意思。
3.解引用操作符
我们将地址保存起来,未来是要使用的,那要怎么使用呢?
在现实生活中,我们使用地址要找到一个房间,在房间里可以拿去或者存放物品。
C语言中其实也是一样的,我们只要拿到了地址(指针),就可以通过地址(指针)找到地址(指针)指向的对象,这里要学习一个操作符叫解引用操作符(*)。
#include<stdio.h>
int main()
{
int n = 10;
int* pn = &n;
*pn = 20;
return 0;
}
上面的代码中*pn = 20;就使用了解引用操作符,*pn的意思就是通过pn中存放的地址,找到指向的空间,*pn就等价于变量n了;所以*pn = 20,就是把n的值改成了20。
那为什么不直接对n赋值改掉n的值,而要通过指针解引用来赋值呢?其实这样就多了一种修改变量值的途径,写代码就会更加的灵活。后面在学习函数传址时会深入理解这里的知识。
4.指针变量的大小
(1) 因为指针变量是用来存储地址的,所以指针变量的大小取决于地址的大小。
通过上面对计算机中地址的深入学习可知,在32位机器下,假设有32根地址总线,每根地址线出来的电信号转换成数字信号后是1或0,那我们把32根地址线产生的2进制序列当做一个地址,那么一个地址就是32个bit位,8个bit位为一个字节,需要4个字节才能存储。
如果用指针变量来存放地址,那么指针变量的大小就得是4个字节的空间才可以。
同理在64位机器下,假设有64根地址线,一个地址就是64个二进制位组成的二进制序列,存储起来就需要8个字节的空间,指针变量的大小就是8个字节。
#include<stdio.h>
int main()
{
//32位平台下地址是32个bit位(即4个字节)
//64位平台下地址是64个bit位(即8个字节)
printf("%zd\n", sizeof(char *));
printf("%zd\n", sizeof(short *));
printf("%zd\n", sizeof(int *));
printf("%zd", sizeof(double *));
return 0;
}
上面的代码在32位平台下运行结果:
上面的代码在64位平台下运行结果:
总结:
●32位平台下地址是32个bit位,指针变量大小是4个字节
●64位平台下地址是64个bit位,指针变量大小是8个字节
●指针变量的大小和类型是无关的,只要是指针类型的变量,在相同的平台下,大小都是相同的。