C语言和内存结合漫谈

内存管理最终是操作系统完成的。
操作系统提供多种机制来让我们应用程序使用内存。
程序在操作系统处登记这块内存的临时使用权限。(别人不能使用),然后使用内存,释放内存(向操作系统归还这块内存的使用权限)
C语言获取内存的三种情况:栈,堆,数据区(.data),代码段,bss段

栈的详解:
局部变量int a
运行时自动分配,自动回收,程序员不需要手工干预。
反复使用:栈内存在程序中就是一块空间,程序反复使用这一块空间(大约4KB空间),
一个函数中定义的变量会在栈中分配,当结束的时候栈指针会移动回原来的位置(这个指针是操作系统操控的)
脏内存:脏数据,不用了以后只是变更栈指针
临时性:函数不能返回栈变量的指针,因为这个指针指向的地址中的值很有可能会改变。‘
栈会溢出:因为操作系统已经给定了栈的大小。
栈溢出代码
void stack_overflow(void)
{
int a[10000000] = {0};
}
迭代
void stack_overflow(void)
{
int a = 2;
stack_overflow();
}

堆内存:
操作系统堆管理器管理:
堆管理器是操作系统的一个模块,堆管理分配方式灵活。
例如:有500M堆内存,有50个线程,然后用450M作为公用堆内存,50M平均分给50个线程,每1M作为线程私有栈。
总的来说450为堆内存,1M为栈内存。

堆程序需要手动申请和释放:需要程序员写代码申请(malloc)和释放(free)
脏内存:跟栈一样
临时性:堆内存只在malloc和free之前属于我这个进程。
void * malloc(size)(malloc方法)
void *其实就是返回堆管理器分配给我本次申请的那段内存空间的首地址。
然后此时我们使用(int *)去解释这个首地址往后的内存解释,解释是4字节的int型分块。
void类型不是表示空类型,而是任意类型,万能类型,当前不确定的类型,在需要的时候再去指定类型。

void main()
{
//需要一个1000个int类型元素的数组
int p = (int )malloc(1000*4);
//检验是否成功
if(NULL==p)
{
printf(“error”);
}
//使用申请到的内存
p = NULL;
p = &a;(这两种是SB行为,会丢失你已经开辟的空间,找不回来了)
//正确使用
*(p+0) = 1;
*(p+1) = 2;
//释放内存
free(p);
//其实free以后依旧可以使用*(p+0) = 1;,但最好不要,因为free以后,堆管理器会分配其它进程用
}

代码段,数据段,bss段
(1)编译器在编译程序的时候,将程序中的所有元素分成一一些组成部分,各部分构成一个段,是可执行程序的组成部分。
(2)代码段.code:程序中的可执行部分,直观的说就是函数堆叠组成的,还有常量。
(3)数据段.data(数据区或者静态区,静态数据区):数据段就是程序中的数据,直观理解就是C语言程序中的全局变量,int,数组等等,还有静态的局部变量(static)(其它局部变量不算,算栈上的)。
(4)bss段(又叫ZI段(zero initial)):bss本质也是属于数据段,区别在于bss段就是被初始化为0的数据段(int b;int b=0;)。
全局的int array[1000](在数据段)和malloc开辟实现的有区别(在堆)。

特殊例子:
char* p = “linux”
*(p+0) = ‘g’//并不能改变,因为其实是const char *p=”linux”,“linux”被放在了代码段。

const型常量:C语言中的const关键字用来定义常量,不能更改,const实现方法:
1.把const常量放到代码段。
2.由编译器来检查,const不被修改,然后放在数据段。(gcc就是这么实现的)

3种获取内存的方法:
相同点:
1.都可以提供内存,都可以用来定义变量给程序用。
不同点:
1.栈对应C中的普通局部变量,别的变量想用栈还用不了(比如全局变量就不行),自动的提供服务的,无法操控栈。
2.堆完全是独立于程序存在和管理,程序需要内存时可以手工去申请malloc,使用完后释放。
3.数据段,还是操作系统来分配的,对于程序来说是对应C语言的中的全局变量和静态局部变量。
堆内存和数据段拥有几乎完全相同的属性,大部分时候是可以替换的,但生命周期不一样。
堆内存生命周期malloc到free,全局程序的生命周期

数组的元素访问和指针的访问虽然有差别,但其实本质是一样的,都是使用指针来实现的。
s.a = 12 //编译器内部 int p = s; (p+0) = 12

结构体为什么要内存对齐:
1.内存本身是物理器件(DDR内存芯片,Soc上的DDR控制器),每次CPU读取4字节只需要一次ldr操作,而如果
读取一个字节,同样需要先读取4字节,然后再读取其中的一个字节。所有就直接读取4个字节然后再后续拼接,截取效率更高。
2.还有很多别的因素,什么cache缓存,MMU,LCD都需要内存对齐。

编译器可以设置内存对齐的规则。
1.32位编译器使用4字节的对齐方式。
struct student
{ //1字节对齐 4字节对齐(刚好8字节,4的整数倍)
int a; //4 4
char b; //1 2(1的整数倍)
short c; //2 2
}
1.首先 结构体student必须是4的整数倍(你设置的对齐字节)
2.结构体中的每个元素本身都必须对齐存放,而每个元素本书都有自己的对齐规则(比如char可以对齐1,2,3,4,都是1整数倍)。
3.编译器考虑结构体存放时,以满足以上2点要求最少内存需要的排布来算。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值