内存漫谈

内存编址和寻址,内存对齐
内存编址方法:
内存逻辑上就是一个一个个字,格子上有编号,编号就是地址
在程序运行时,CPU只认识内存地址,不关心地址所代表的空间在哪里
关键:内存编址是以字节为单位的
我随便说一个数字7,说这个数字是一个内存地址,然后问你这个内存地址对应的空间有多大?答案是固定的,就是一个字节

内存和数据类型的关系:
C语音的基本数据类型:char,short,int,long,float,double
int (这个整就体现了它和CPU本身的数据位宽是一样的)比如32位CPU,就是32位

在32位系统中定义变量最好使用int,因为这样效率高,原因就在于32位的系统本身配合内存等也是32位,这样的硬件配置天生适合定义32位的int类型变量
效率最高,也能一定8位的char,或者16位的short,但实际上访问效率不高。
在32位环境下,我们实际定义bool类型变量(实际只需要1个bit就够了),但其实都是用int来实现bool,也就是说我们定义一个bool b1;时,
编译器实际帮我们分配了32位的内存来存储这个bool b1,编译器这么做实际上浪费了31位内存,但好处就是效率高。

内存对齐:
定义一个int a
思路:
1:0 1 2 3(对齐)
2:1 2 3 4(非对其访问)
逻辑上都没问题,但从硬件角度看0123是一个单元,在硬件上是合适的,效率就高
因为硬件都是提供兼容性问题,提供非对齐访问,但是效率要低很多

一个int a[]数组:
a代表数组的首地址4,首单元为7654:
ba98
7654
3210

C语言如何操作内存:
int a;//gcc会申请一个32位4个字节的内存地址,并用a符号表示,申请的内存地址只有编译器知道。
a = 5; //编译器会把5这个值丢到a绑定的内存空间里
a +=4;//编译器会先把a原来的值读出来,然后再加后再把结果放到内存格子里
C语言中数据类型的本质的含义:表示一个内存格子的长度和解析方法:
例如:(int *)0,指定了以0为开始的地址向后延伸32位,(char)0表示延伸1个字节内存格子
解析方法:
例如:int 0x30000000 0x30000001 0x30000002 0x300000034个字节用int形式来解析
(float)0x30000000 0x30000001 0x30000002 0x3000000344个字节用float来解析

C语言中的函数,函数是一段代码的封装,函数名的实质就是这一段代码的首地址。函数名的本质就是内存地址

用指针来简介访问内存:
关于类型,不管是int,float还是指针类型,还是int *类型:
类型只是对后面数字或者符号(代表的是内存地址)所表征的内存的一种长度和解析方法规定而已:
指针变量和普通变量没有什么区别,例如int a和int* p,a和p都代表了内存地址(0x30000000),
int a表示a是int型,所以a的长度是4字节,解析方法是按照int的规定来;
int* p表示p是int*类型,所以长度是4字节,解析方法是按照int*规定来(0x30000000开头的连续4个字节中存储了一个地址,这个地址所代表的内存单元存放的是
一个int类型的数)
引申:
数组管理内存的变量其实没有本质区别,只是符号的解析方法不同
int a; //分配了4字节,把首地址给了a
int b[10] //分配了40字节,把首元素的首地址给了b

数据结构这门学问的意义:
数据结构就是研究数据如何组织(在内存中排布),如何加工的学问

结构体内嵌指针实现面向对象:
struct s
{
int age;
void (*pFunc)(void); //函数指针,指向void func(void)这类的函数
};
这样使用的结构体就可以实现面向对象。
这样包含了函数指针的结构就类似于面向对象中的class,结构体中的函数指针类似于class中的成员方法。

内存管理之栈(stack):
栈是一种数据结构,C语言中使用栈用来保存局部变量。栈是被发明出来管理内存的。
栈管理内存的特点:
先进后出,FILO
C语言中的局部变量就是用栈了实现的。
我们定义一个局部变量int a,编译器就在栈中分配一段空间(4字节)给局部变量用,分配时栈顶指针会移动给出空间,给局部变量a用的意思就是,
将这4个字节的栈内存的内存地址和我们定义的局部变量名a给关联起来,对应栈的操作就是入栈。
注意:这里栈的移动和内存分配是自动的(栈自己完成,不用我们去写代码)
然后等我们函数退出的时候,局部变量要灭亡,对应栈的操作就是出栈。

栈的优点:栈管理内存,好处就是方便,分配和最后回收都不用程序员操心,C语言自动完成。
分析一个细节,C语言定义局部变量时如果未初始化,则值是随机的,为什么?
定义一个局部变量,栈指针只是移动,并没有去清空内存中的值,而栈是反复使用的,所以都是脏数据

栈的缺点(预定栈大小不灵活,怕溢出):
栈是有大小的,不好设置,
其次,栈的溢出危害很大,所以我们在C语言定义局部变量时不能定义太多或者太大,例如int a[100000];使用递归解决问题时,注意递归收敛。

内存管理之堆(heap):
堆是一种内存管理方式,内存管理对操作系统来说是一件非常复杂的事情,因为内存容量大,其次内存需求在时间和大小块上没有规律,当有几十个进程运行时更复杂。
堆这种内存管理方式的特点就是自由(随时申请,释放,大小块随意)。堆内存是操作系统划归给堆管理器(操作系统中的一段代码,属于操作系统的内存管理单元)来管理,
然后向使用者(用户进程)提供API(malloc和free)来使用堆内存。

我们什么时候使用堆内存?
需要内存容量比较大时,需要反复使用及释放时,比如int a[1000],这样最好先malloc内存,因为占用了4000个字节,还有很多数据结构,链表,二叉树等

堆的特点:
1.容量不限(常规使用的需求容量都能满足)
2.申请即释放都要手工进行,就是malloc及free,如果程序员申请内存并使用后未释放,这段内存就丢失了(在堆管理器的记录中,这段内存仍然
属于你这个进程,但是进程自己又以为这段内存已经不用了,再用的时候又去申请新的内存块),称为内存泄漏。

C语音操作堆内存的接口
释放:void free(void* ptr)//直接传一个指针就可以
堆内存的申请:malloc,calloc,realloc
void* malloc(size_t size) //直接申请10字节 malloc(10),长度
malloc(40) == malloc(10*sizeof(int))//32位下的是相等的,64位下是80字节了

void* calloc(size_t nemmb,size_t size)//size_t nemmb,个数
calloc(10,4) calloc(10,sizeof(int))

void* realloc(void* ptr,,size_t size)

数组定义时必须同时给出数组元素个数,而且一旦定义再无法更改。在java语言中,有一些技巧可以更改数组大小,
但都是障眼法,都是先申请另外一个数组,然后把原来的数组的值放进来,然后再释放掉原来的数组。

堆内存申请时必须给定大小,一旦申请完成,需要更改使用realloc接口,实现和上面的一样。

堆的优势和劣势:管理大块内存,灵活,但容易内存泄漏

冯诺依曼结构:数据和代码放在一起,运行程序时,应用程序的代码和数据都在DRAM中。
哈弗结构:数据和代码是分开的,代码放在Flash中,数据放在RAM(不需要初始化,所以只有SRAM)。
如果只有代码没有数据,则只需要在Flash中运行就可以了。不需要RAM

数据:全局变量和局部变量,常量
在gcc中存在内存中
在单片机中,在flash(闪存)中
内存和闪存的关系:闪存式内存中的一种,但是内存需要供电,而闪存不需要供电维持

动态内存DRAM(需要初始化才能用,然后又分好多代,根据不同需要求,比如手机用的LPDDR低功耗)和静态内存SRAM(不需要初始化,可以直接使用)

为什么需要内存:
因为内存是用来存储可变数据的,

怎么管理内存,如果有操作系统:
操作系统怎么管理:
因为内存很大,所以操作系统把内存分成1个1个页面,(其实就是一块,一般是4KB),然后以页面为单位来管理,
页面内用更细小的方式以字节为单位管理,其实不需要了解这些细节,只需了解操作系统给我们提供了内存管理的
的一些接口,我们只需要用API管理即可。
譬如C语音使用malloc free这些接口来管理内存。

如果在没有操作系统时,程序需要直接操作内存,编程者需要计算内存的使用和安排,如果编程者不小心把内存用错,错误结果自己承担。

从语言角度来看内存:
汇编:没有任何内存管理,直接操作某个内存地址。
C语言:C语言中编译器帮我们管理直接内存地址,我们都是通过编译器提供的变量名比如(int a),a就是访问内存地址,而不是直接使用内存地址。
如果需要大块内存,可以通过API(malloc free)来访问系统内存。

C++语音对内存的使用进一步封装,我们可以使用new 来创建对象(其实就是为对象分配内存)
但C++中内存的管理还是靠程序员自己来做,new后需要delete

java/C# 等语言,这些语言不直接操作内存,而是通过虚拟机来操作内存。这样虚拟机作为我们程序员的处理,来帮我们
处理内存的释放工作。

什么是内存:
从硬件角度:就是内存条,根据不同硬件实现原理分为SRAM和DRAM
从逻辑角度:内存就是一种东西,可以随机访问Random-Access Memory(RAM),随机访问意思是(只要给一个地址,就可以访问这个内存地址)
并且可以读写(当然逻辑上可以限制只读,只写)

逻辑上来说,内存可以有无限大,但是现实中是有限制的:
譬如32位系统,限制就为4G,32位系统指的是32位数据线,但是一般地址线也是32位,这个地址线32位内存地址就只能有32位二进制,所以限制就为4G
32位逻辑图,16位,8位 每一个单元格8位,8位也可以通过并联来变成32位
ba98 54 2
7654 32 1
3210 10 0
为啥这样实现都是硬件的问题,这样处理起来更简单,很多都受限于硬件特性

内存单元大小单位有4个:位(1bit),字节(8bit),半字(一般16bit),字(32bit)
位和字节是不变的

字和半字
有个概念就行了

汇编:
一个汇编指令代表一串2进制
汇编语言是跟CPU有关的,有些汇编指令在另外一个CPU不适用。
为了可移植性,就发明了高级语言,C语言
汇编通过汇编器翻译将这些源代码变成二进制,然后给CPU运行。
C语言通过编译器翻译将这些源代码变成二进制,然后给CPU运行。

编辑语言的关键就是编译器,yacc和lex工具就是用来发明编程语言的。

预处理器:
预处理器中处理程序注释,空行,文件头#include

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值