C语言基础5(动态内存)

变量的作用域(使用范围)和生命周期
变量必须先声明才能使用
1.全局变量
(1)在全局域定义的变量,所有的函数外面定义的
(2)所有的函数都可以访问(除了静态全局变量:只能在本文件中访问)
(3)全局部变量如果不初始化,则自动初始为0
(4)如果在一个函数中要使用定义在其它文件中的全局变量
extern int a;//声明一个在其它文件中定义的全局变量a
函数也一样可以这样声明
extern告诉编译器,变量或者函数在其它文件中定义
(5)生命周期 是整个程序运行期间
编译的过程一个一个.c文件进行编译

2.局部变量
	(1)在函数内部定义的变量  
	(2)局部变量只能在本函数内部访问
	(3)形参也是局部变量   只是在函数调用时用实参给形参进行赋值
	(4)局部变量不会自动初始化  如果程序员不对局部变量初始化 那么得到的是垃圾值
	(5)局部变量的生命周期 函数运行期间(static  静态局部变量除外)
	     函数在调用时会开辟一块内存用于存储局部变量
		 当函数调用结束之后,该内存会被回收
		 所以函数如果返回指针类型: 不能返回局部变量的地址
		    地址:这个局部变量的地址有可能被其他值占用,也可能被取消权限

3.块变量
	(1)在语句块内部定义的变量
	(2)有些书上也把块变量归为局部变量
	(3)块变量只能在语句块中访问
		for() while if()
		只要有{}就是语句块
	(4)块变量不会自动初始化  未初始化是垃圾值 
	(5)生命周期 语句块执行期间
在同一个作用域下,变量不能重复定义
在不同的作用域下,能够定义同名的变量

局部优先原则:
	局部变量会隐藏全局同名的变量
	块变量会隐藏同名的局部变量

如何访问同名的全局变量:
用全局指针
用函数返回
{
	 extern 声明全局变量;
	 //访问同名的变量时 访问到的是全局的变量
}
在一个项目中,不要使用太多的全局变量,全局变量越少越好
全局变量不利于项目开发
	局部变量跟全局变量冲突,出错不容易发现
	也不利于程序逻辑的理解

Linux下C语言程序(进程)的内存分布

程序(.out  .exe)存储在磁盘上的静态代码
运行程序的时候 会把 静态代码 加载到内存区域  形成内存映像
linux32位系统
	4G的内存映像( 虚拟内存 )	虚拟内存映射到物理内存(操作系统)
	寻址  
64位系统
	2^64  

变量的存储域(内存位置)
定义的位置
auto 自动的
auto int x;//默认就自带auto 省略auto(C++自动类型推导)

static 静态的
存储位置 全局区 静态全局区 如果初始化存储在数据段 未初始化BSS段值为0
静态局部变量 普通局部变量的区别?
1.存储位置不一样
静态局部变量存储在全局数据区
普通局部变量存储在栈区
2.生命周期不一样
静态局部变量生命周期是整个程序 一直存在
普通局部变量生命周期是函数调用期间
静态局部变量的初始化语句只会执行一次,每次调用完之后会保留上一次的结果
3.作用域一样

静态全局变量  普通全局变量的区别?
	1.存储区域一样
	2.生命周期一样
	3.作用域
		静态全局变量只允许在本文件中使用 
		普通的全局变量在其它文件中可以使用  使用用extern进行声明
	
静态函数: 只限制在本文件中调用

register 寄存器的
寄存器的数量有限
arm处理器只有37个
register int x;//申请把x变量作为寄存器变量
(1)寄存器变量存储在寄存器中,不在内存区域,不能对寄存器变量取&
寄存器变量没有地址
(2)寄存器变量一般要满足机器的字长(word)单位的长度
char short int
double就不行
(3)寄存器变量的读取效率最高 频繁进行读写的变量作用寄存器变量比较好
(4)register只是一种请求,寄存器变量有限,不是所有有register都实质上成为了寄存器变量

volatile 易变的
使用在特定的场合: 多线程
声明的变量表示该变量的值有可能被其它线程修改
随时可能发生变化的值
表示取该值运算时不能直接从寄存器上面取,必须去内存中取

volatile int x = 10;
//算x的平方
int n = x;   //不能直接x * x 
int res = n*n;

extern 外部的
声明外部已经定义过的变量 和 函数

const 只读的
用const修饰的变量称为常量

常量: 
	整数数值:1  2   3  0 
	浮点数值:3.14  3E10  3.14e1
	         3e1.2     3e  错误
	字符:  'a'   '!'  '\\'
	字符串字面值:  "Hello"  "main"
	枚举常量:   enum Week{MON,TUE,WED,THR,FRI,STA,SUN};
	const定义的

//定义常量时 定义时并且初始化
const int x;
x = 1;//错误的

cosnt int x = 1;
int const y = 2;

const char * p1;// const修饰*p1 p1只读 p1可以修改
char const * p2;// const修饰
p2 *p2只读 p2可以修改
char * const p3;// const修饰p3 p3只读 *p3可以修改
const char * const p4;//
char const * const p5;// const修饰p5 p5, p5和p5都只读

指针常量:本质是一个常量 指针是一个常量
char *const p3;
常量指针:本质是一个指针 指针指向一个常量
const char *p1; char const *p2;

函数指针 指针函数 数组指针 指针数组

typedef 给类型取别名
(1) typedef old_type_name new_type_name;
typedef unsigned int size_t
typdef int * int_ptr;
int* a,b;
int_ptr p1,p2;
typedef int I;
unsigned I a;//不可以的
(2) 定义函数指针类型
typedef 返回值类型 (*函数指针类型名)(参数列表);
void (*f)(void); //变量 函数指针变量
typedef void (*F)(void); //类型 函数指针类型
(3) 定义数组类型
int brr[5];//brr是一个变量
typedef int arr[5];//arr是一个类型 int [5]

认识一个变量的方法:
首先找到标识符,从标识符往右找,
如果先遇到[]说明是数组(接下来考虑数组中元素的类型)
如果先遇到()说明是一个函数 函数需要分析返回值类型 和 参数列表
如果先遇到) 需要往右找,右边只会有* 说明是一个指针 需要分析指针的类型

把标识符和f[n]或者f(xxx)或者(*f)看作一个整体 按照上面的方法继续分析

指针与数组名的区别?
指针即内存地址 数组名也代表一个内存地址
数组名是数组首元素的地址
数组名本质上是指针
sizeof(数组) 整个数组的内存空间
sizeof(指针) 4/8
数组名是一个指针常量,即数组名不能放在=左边
p = p+1; p++;
arr = arr+1; arr++;//不行的
数组和指针都可以取地址
指针取地址即二级指针 数组名取地址 即数组指针
数组名+整数 === 指针+整数
数组名[整数] === 指针[整数]

一维数组作为参数传递时,退化成了指针
func(int arr[10]){//在形参形表中的数组 完全和指针一样
	sizeof(arr)
	arr = arr+1;
}

const的作用?
1.指针形参用const修饰 防止函数内部对实参内容进行修改 增加代码的健壮性
2.定义只读变量 常量 指针常量 常量指针
3.加上const修饰,防止意外修改

权限可以变小 但不能提升(C语言中还有可能只是警告,但是C++中全部报错)
int n = 0;
const int x = 0;
const int *p = &n;//权限受到了限制

提升权限 
int *p1 = &x;//提升权限 编译器要么是报错 要么是报警告

static的作用? C++中static还有很多作用
1.修饰局部变量 改变存储位置 改变了生命周期
2.修饰全局变量 改变作用域范围
3.修饰函数 限定作用范围

动态内存: 堆区 手动申请手动释放
包含头文件 #include<stdlib.h>
void *malloc(size_t size);
申请size个字节堆内存
返回申请好的堆内存的首地址
这片内存用于存储什么数据完全取决于程序员自己
程序员只需要转换成自己想要的类型即可
如果申请失败返回 返回 NULL
int *p = malloc(4);//只能访问4个字节
char p1 = malloc(4);
int p2 = malloc(410);//一维数组
int (p3)[5] = malloc(54
6);//二维数组 6行5列

void free(void *ptr);//释放堆内存
参数ptr的值一定要是申请动态内存时返回的值
不能释放一个不是堆内存的指针
同一块堆内存不能多次释放
堆内存用完之后,立即free
内存泄露: 动态内存没有释放 或者 忘记释放
动态内存如果不手动释放,则会一直被占用

动态内存在使用时,要注意只能使用申请大小的空间,不能越界访问或者修改
动态内存越界访问也是非常危险

void calloc(size_t nmemb,size_t size);
用于申请动态内存
malloc只有一个参数 size 即申请堆内存字节大小
calloc申请nmemb个size字节大小的连续内存 nmemb
size
malloc申请的内存可能有垃圾值 calloc会全部清0
失败返回NULL
在使用堆内存之前一定要判断该内存是否申请成功

void *realloc(void ptr,size_t size);
调整堆内存大小
int p = malloc(104);
//如果发现内存不够用了
p = realloc(p,100
4);//扩大内存 之前的有的值依然会保留
参数ptr是之前申请动态内存返回的地址
参数size是需要把动态内存调用为size个字节
如果需要使用这个内存,必须重新接收返回值
因为参数ptr和realloc返回值可能不一样
情况1:ptr要扩容,后面有足够的空余的内存,
直接把后面划到此区域,返回ptr
情况2:ptr要扩容,后面没有足够的内存,重新申请一块新的内存,
把之前的数据拷贝到些内存,并返回新的首地址
如果失败返回NULL

值得注意的问题:
1.申请内存是以页为单位 一次性申请33页
1页 = 4kB = 4*1024 = 4096 Byte
2.即使把所有的堆内存都释放掉了,依然会保留最开始的33页

要求:
1.申请多少使用多少 不要越界
2.释放的内存不能再使用了

int *p = malloc(4); 不仅只分配了4个字节 分配了16个字节
有12个字节的内存控制块 用于记录该块内存的大小 是否空闲 下一块内存的位置
内存控制块的信息不能被破坏,一旦破坏,后果很严重

每一个动态内存都需要12个字节的控制信块
所以为了内存的效率着想,堆内存一般用于大片内存
小内存不要去堆中申请

内存碎片:
堆内存不断重复申请和释放,
有一些大的内存块被分配给小的内存空间使用,
有一部分内存无法使用到在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值