十年网站开发经验 + 多家企业客户 + 靠谱的建站团队
量身定制 + 运营维护+专业推广+无忧售后,网站问题一站解决
计算机中所有的数据必须存放在内存中,不同的类型的数据占用的内存字节也不同,int型占4字节,char型占一字节,为了正确访问这些数据,必须为每个一个自己都编上号码,就像仓库一样,每个仓库都会有自己的编号,来定位到具体的仓库。没有字节的编号都是唯一的,根据编号就可以准确的找到某一个字节。
如: char a =‘a’;
这一句话中a就是一个指针地址他指向了内存中一个char型1字节的内存空间 ,而这个字节内存储的数据是‘a’,所以char a中a存储的数据并不是‘a’而是内存中1个字节空间的内存地址。是用十六进制表示的一个内存地址编号,如:0x1000;
如图是4G内存中每个字节的编号(以十六进制表示):
int i ;
0x0000 0x0001 0x0002 0x0003
0xFFFFFFFD 0xFFFFFFFE 0xFFFFFFFF 0xFFFFFFFC
白银ssl适用于网站、小程序/APP、API接口等需要进行数据传输应用场景,ssl证书未来市场广阔!成为创新互联的ssl证书销售渠道,可以享受市场价格4-6折优惠!如果有意向欢迎电话联系或者加微信:18980820575(备注:SSL证书合作)期待与您的合作!
我们将内存中字节的编号称为地址(Address)或指针(Pointer)。地址从0开始依次增加,对于32位环境中,程序能够使用的内存为4GB,最小的地址为0,最大的地址是0XFFFFFFFF.因为十六进制0XFFFFFFFF对应的二进制就是11111111111111111111111111111111 。
下面的代码演示了如何输出一个地址:
#include
int main(){
int a=100;
char str[20]=”zheshiyizifuchuan”;
printf(“%#X,%#X\n”,&a,str);
return 0;
}
运行结果:
0XBF80E1E4,0XBF80E1F8.
这里解释一下为什么他们的间隔是4,因为int型a在内存中占四字节,每个字节都会有自己独特编号指针,a在内存中的分布是
0XBF80E1E4 0XBF80E1E5 0XBF80E1E6 0XBF80E1E7
而char str[20]数组中的内存分布是
0XBF80E1F8 0XBF80E1F9 0XBF80E1E2 ……此处省略中间的地址… 0XBF80E1V8
而取str的内存地址是,取的是char str[20]数组中的首元素的地址,所以str的内存地址是0XBF80E1F8。
%#X表示以十六进制形式输出,并附带前缀0X。a是一个变量,用来存放整数需要在前面加&来获取它的地址;str本身就表示字符串的首地址,不需要加&。
C语言中有一个控制符 %p,专门用来打印出以十六进制形式表示的变量内存地址,不过,%p的输出格式并不统一,有的编译器带0X前缀,有的不带,所以此处我们并没有采用
一切都是地址(指针)
C语言用变量来存储数据,用函数来定义一段可以重复使用的代码,他们最终都要放到内存中才能供CPU使用。
注释:C语言中数据的存储都是在声明的变量中,换句话说就是声明的变量,变量的指针指向的是内存的数据存储层,如果想要存储一条数据就需要声明一个变量而变量不存储数据,变量只存储声明的这个变量的类型字节空间在内存中的编号,而数据存储在声明的类型的字节空间中,变量只是一个指向这块空间的指针。变量的指针是指针内存数据层,而函数其实本质上也是一个指针,函数名也是一个指针。而函数名指向的 内存空间是代码层。其实声明一个函数就是在内存的代码层中声明了一块空间在存储编写的代码,而函数名其实内存储的其实就是指向这一块内存空间的地址指针。所以从这一层面上来看的话,程序中不管是变量还是函数都是存储在内存中的,而我们编写的变量名,函数名,数组名,字符串名,其实都是指向内存空间的字节编号,而他们的存储空间在内存中也会在不同的层中,这也是方便cup的读取。而cup读取执行程序时,会根据变量名和函数名提供的内存字节地址准备的去不同的内存层空间中读取不同的数据。
数据和代码都是以二进制的形式存储在内存中,计算机无法从格式上区分某块内存到底存储的是数据还是代码,当程序被加载到内存后,程序系统会给不同的内存指定不同的权限,拥有读取和执行权限的内存块就是代码,而拥有读取和写入权限(也可能只有读取权限)的内存块就是数据。
注释:程序中声明的变量和代码最终都会转为二进制代码存储在计算机的内存中,但是计算机无法区分出哪里的内存存储的数据是数据,哪里的是代码,所以需要程序系统给不同的内存空间设定不同的权限,来区分内存空间中存储的数据还是代码,这也是内存出现不同层的原因。
CPU只能通过地址来取得内存中的代码和数据,程序在执行过程中会告知CPU要执行的代码以及要读写的数据的地址。如果程序不小心出错,或者开发者有意为之,在CUP要写入数据时,给它的一个代码区域的地址,就会出现内存访问错误,这种内存访问错误会被硬件和操作系统拦截,强制程序崩溃,程序员没有挽救的机会。
注释:程序在执行时CPU只能通过地址去内存中读取数据,而代码和数据是存放在不同的内存层中如果程序在编写中设置的变量地址或者函数地址的数据层不对时,那么CPU在读取数据或代码时会因为权限的问题而被硬件和操作系统拦截,从而内存数据读取失败导致程序崩溃,因为这个过程是编写好的而且程序执行非常快的,所以程序员没有计划挽救。
CPU访问内存时需要的是地址,而不是变量名和函数名!变量名和函数名只是地址的一种助记符,当源文件被编译和连接成可执行程序之后,他们都会被替换成地址。编译和连接过程的一项重要任务就是找到这些名称所对应的地址。
注释:这句话很重要,这句话也是地址的核心,变量名在声明时会有一个自己独特的地址,而程序在编译时也会把声明的变量名转换为指针,在CPU访问内存是就是需要的我们之前声明的变量转换之后的地址指针。而编译就是负责这个转换的过程。
假设变量 a b c 在内存中的地址分别是0X1000 0X20000 0X30000,那么加法运算c=a+b;
将会被转成类似下面的形式:
0X3000=(0X10000)+(0X20000);
()表示取值操作,整个表达式的意思是,取出地址0X10000和0X20000上的值,将他们相加,把相加的结果赋值给地址为0X30000的内存。
变量名和函数名为我们提供了方便,让我们在编写代码的过程中,可以使用易于阅读和理解的英文字符串,不用直接面对二进制地址,那场景简直让人崩溃。
需要注意的是,虽然变量名,函数名,字符串名,和数组名,在本质上是一样的,他们都是地址的助记符,但在编写代码的过程中,我们认为变量名是表示的是数据本身,而函数名和数组名表示的是代码块或数据块的首地址。
注释:这里的原因是因为变量名指向的是一个一种数据类型的内存空间地址,而函数名,字符串名,数组名,指向的是一块内存中连续的数据类型空间的第一个字节的地址。
本文章参考c.biancheng.net.中c语言指针是什么的博文和添加自己的主观想法完成的编写。
原文:http://c.biancheng.net/view/1990.html