【C语言】动态内存申请 | 内存区域的划分 | malloc、calloc、free函数的使用

一 内存区域划分

1 代码区

存代码

2 常量区

存常量:数值常量 字符常量 字符串常量 符号常量(define) 地址常量…

3 全局区(静态全局区)

关键词:

  1. 静态变量(static) static:静态
  2. 全局变量
#include<stdio.h>

int b = 0;                 //普通全局变量
static int d = 0;          //静态全局变量

int main() {

	int a = 0;             //普通局部变量(主函数范围内)
	static int  c = 0;     //静态局部变量

	return 0;
}

作用与区别:

  1. 作用域 : 起作用的区域
  2. 生存周期(生命周期) : 起作用的时间段
#include<stdio.h>

//全局变量生存周期:程序开始到程序结束
int b = 0;                 //普通全局变量
	//普通全局变量作用域(项目作用域):作用域为整个项目,不同文件时用extern声明 
	//extern: extern + 完整定义
    //
static int d = 0;          //静态全局变量
	//静态全局变量作用域(文件作用域):作用域为当前文件

int main() {
	//作用域: 当前语块(变量定义到大括号结束)
	int a = 0;             //普通局部变量
	//生存周期:当前语块
	static int  c = 0;     //静态局部变量
	//生存周期:程序开始到程序结束

	return 0;
}

void  fun() {
	a = 10;               //报错:未定义标识符
	b = 10;
	c = 10;               //报错:未定义标识符
	d = 10;
	//a和c都为局部变量(作用域在变量定义到当前函数结束)
}

总结:

  1. 包含全局或静态的变量:生命周期皆为程序开始到程序结束,否则生命周期为当前语块(存储于静态全局区)
  2. 静态全局区:从程序开始系统自动分配内存,到程序结束系统自动回收内存
4 栈区

存储普通局部变量, 从定义开始系统自动分配内存,出了函数系统自动回收临时内存

5 堆区

由程序员手动申请,手动释放

二 void* 指针

空类型指针

#include<stdio.h>

int main() {
	void* p = NULL;
	p + 1; //错误:表达式必须是指向完整对象类型的指针
	p++;   //错误:表达式必须是指向完整对象类型的指针

	int* pn = NULL;
	pn = p;//错误:不能将"void*"类型的值分配到"int*"类型的实体
	p = pn;//正常执行

	short* psh = NULL;
	p = psh;

	*p;    //错误
	return 0;
}

1 不能偏移

2 不能自增自减

3 可以接受任意其他类型的指针

4 不能直接给其他类型的指针值(可以强转)

5 不能直接取内容

void*类型的指针不知道自己的长度(不完整)

三 动态申请内存

  1. 申请释放的方法

    头文件:

    #include<stdlib.h>
    

    申请

    void* malloc(size_t _Size);               // 参数:要在堆区申请的字节数
    void* calloc(size_t _Count, size_t _Size);// 参数:类型长度, 个数
    

    释放

    void free(void* _Memory);                 // 参数:地址(释放内存的首地址)
    

    内存申请之后必须释放. 释放之后指针必须置空.

  2. 内存泄露和野指针

    内存泄露: 申请的内存没有进行释放

    野指针: 指针指向了不该指向的地方

  3. 简单应用举例

    1.1 使用malloc申请一个int类型大小的内存(4字节)

    	//1. 申请
    	int* p = (int*)malloc(sizeof(int));//void* 给值要进行强转
    	//2. 使用
    	*p = 100;
    	//3. 释放
    	free(p);
    	//4. 置空
    	p = NULL;
    

    1.2 使用malloc申请十个int类型大小的内存(40字节)

    	//1. 申请
    	int* p1 = (int*)malloc(sizeof(int) * 10);
    	//2. 使用
    	if (p1) { //未申请成功,函数返回NULL 由此判断是否申请成功
    		//指针偏移使用
    		p1[0] = 1;
    		p1[2] = 10;
    	}
    	else printf("申请失败");
    	//3. 释放
    	free(p1);
    	//4. 置空
    	p1 = NULL;
    

    2.1 使用calloc申请一个int类型大小的内存(4字节)

    	//1. 申请
    	int* p2 = (int*)calloc(sizeof(int), 1);
    	//2. 使用
    	*p2 = 100;
    	//3. 释放
    	free(p2);
    	//4. 置空
    	p2 = NULL;
    
    完整代码
    #include<stdio.h>
    #include<stdlib.h>
    int main() {
    	//1.1 使用malloc申请一个int类型大小的内存(4字节)
    	//1. 申请
    	int* p = (int*)malloc(sizeof(int));//void* 给值要进行强转
    	//2. 使用
    	*p = 100;
    	//3. 释放
    	free(p);
    	//4. 置空
    	p = NULL;
    
    	//1.2 使用malloc申请十个int类型大小的内存(40字节)
    	//1. 申请
    	int* p1 = (int*)malloc(sizeof(int) * 10);
    	//2. 使用
    	if (p1) { //未申请成功,函数返回NULL 由此判断是否申请成功
    		//指针偏移使用
    		p1[0] = 1;
    		p1[2] = 10;
    	}
    	else printf("申请失败");
    	//3. 释放
    	free(p1);
    	//4. 置空
    	p1 = NULL;
    
    	// 使用calloc申请一个int类型大小的内存(4字节)
    	//1. 申请
    	int* p2 = (int*)calloc(sizeof(int), 1);
    	//2. 使用
    	*p2 = 100;
    	//3. 释放
    	free(p2);
    	//4. 置空
    	p2 = NULL;
    	return 0;
    }
    
  4. 动态数组

    动态数组(并非数组,而是堆区申请的内存)

    类一维数组
    #include<stdio.h>
    #include<stdlib.h>
    int main() {
    
    	//1. 申请
    	int len = 10;
    	int* arr = (int*)malloc(sizeof(int) * len);
    	//2. 使用
    	if (arr) { //未申请成功,函数返回NULL 由此判断是否申请成功
    		//指针偏移使用
    		for (size_t i = 0; i < len; i++) {
    			arr[i] = i;
    		}
    		for (size_t i = 0; i < len; i++) {
    			printf("%2d",arr[i]);
    		}
    		printf("\n");
    	}
    	else printf("申请失败");
    	//3. 释放
    	free(arr);
    	//4. 置空
    	arr = NULL;
    	return 0;
    }
    
    类二维数组
    #include<stdio.h>
    #include<stdlib.h>
    int main() {
    	// 申请类似于int arr[3][4];
    	//1.  申请
    	//1.1 申请3个指针
    	int** pp = (int**)calloc(sizeof(int*), 3); //函数参数用一级指针,函数应返回二级指针
    	//1.2 分3次申请4个int大小的内存
    	for (size_t i = 0; i < 3; i++) {
    		pp[i] = (int*)calloc(sizeof(int), 4);
    	}
    	// ==> 不一定像真正的二维数组一样内存连续
    
    	//2. 使用
    	for (size_t i = 0; i < 3; i++) {
    		for (size_t j = 0; j < 4; j++) {
    			pp[i][j] = i * 10 + j;
    			printf("%3d", pp[i][j]);
    		}
    		printf("\n");
    	}
    	printf("\n");
    	//3. 释放
    	free(pp);
    	//4. 置空
    	for (int i = 0; i < 3; i++) {
    		pp[i] = NULL;
    	}
    	return 0;
    }
    
    简单数组扩容

    思路:

    1. 给定默认长度

    2. 添加数据

    3. 如果装满了,自动扩容

    #include<stdio.h>
    #include<stdlib.h>
    int main() {
    	// 1. 给定默认长度
    	// 2. 添加数据
    	// 3. 如果装满了,自动扩容
    
    	// 默认初始长度:10
    	int len = 10;
    
    	// 申请长度为len(10)个int大小的内存
    	int* p = (int*)calloc(sizeof(int), len);
    	int* ptemp = p;       // 保存p当前地址
    
    	int i = 0;            // 下标
    	int num = 0;          // 用来接收每一次的输入
    
    	// 添加数据(重复输入)_结束标志: -1
    	while (scanf_s("%d", &num), num != -1) {
    		// 1 长度足够
    		if (i < len) {
    			ptemp[i++] = num;
    		}
    		// 2 长度不够,需要扩容
    		else {
    			// 自定义扩容规则: 变为原来2倍
    			len *= 2;
    			p = (int*)calloc(sizeof(int), len);     //重新申请一段内存
    			// 拷贝之前内存中的数据
    			for (size_t j = 0; j < i; j++) {
    				p[j] = ptemp[j];
    			}
    			p[i++] = num;      //添加当前需要添加的数据
    			free(ptemp);
    			ptemp = p;         //ptemp 重新指向新内存
    		}
    	}
    	// 检验
    	for (size_t j = 0; j < i; j++) {
    		printf("%3d",ptemp[j]);
    	}
    	printf("\n当前内存长度:%d\n当前数据个数:%d\n",len,i);
    
    	free(p);      //释放
    	// p和ptemp为同一段内存,切勿重复释放
    	p = ptemp = NULL;
    
    	return 0;
    }
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

MuShan-bit

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值