2020.10.27 15课 内存区域划分与动态内存管理malloc calloc free

一 内存区域划分

一) 区域划分

代码区 常量区 栈区 堆区 静态全局区

1 代码区

二进制代码

2 常量区(文字常量区)

字符串

3 栈区

由编译器自动分配和释放,存放函数的参数的值,局部变量等.

4 堆区

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

5 全局区(静态区)

全局变量和静态变量的存储是在一起的

#include <stdio.h>
// 普通全局变量:定义在函数外
int num;
// 静态全局变量:定义在函数外 并且使用static进行修饰
static int hp;
int main()
{
	// 普通局部变量
	int a;					// a	栈区
	// 静态局部变量
	static int mp;
    
	char str[] = "abcd";	// str	栈区	abcd\0 常量区
	float* p;				// p	栈区
	char* pstr = "abcd";	// pstr	栈区	abcd\0 常量区
							// 编译器可能将两个abcd\0优化为一份内存
	return 0;
}

二) 生存周期,作用域

普通全局变量(外部变量)

作用域:从定义开始到(源)文件结束

生存周期:从程序执行到程序结束

普通局部变量

作用域:函数(复合语句)内部==>当前大括号

生存周期:从函数调用开始 到函数调用结束

static局部变量

作用域:同普通局部变量

生存周期:同普通全局变量

static全局变量

作用域:被编译文件的剩余部分

生存周期:同普通全局变量

补充:

1 当一个源程序由多个源文件组成时,非静态的全局变量在各个源文件中都是有效的.

2 静态全局变量只在当前文件有效

static

#include <stdio.h>
/*
static:静态的
特点:只会定义一次
*/

int func1()
{
	int a = 0;
	a++;
	return a;
}
int func2()
{
	static int a = 0;		// 只会定义一次 
	a++;
	return a;
}

int main()
{
	func1();
	func1();
	printf("%d\n", func1());		// 1

	func2();
	func2();
	printf("%d\n", func2());		// 3 

	return 0;
}


二 动态内存管理

使用一个头文件: #include <stdlib.h>

使用三个函数: malloc calloc free 实现申请内存和释放内存

一) malloc

#include <stdio.h>
#include <stdlib.h>

int main()
{
	// malloc
	// 函数原型: void * __cdecl malloc(_In_ _CRT_GUARDOVERFLOW size_t _Size);
	// 返回:void*	参数:size_t(unsigned int) 需要申请的字节数
	// 1 使用malloc申请一个int类型大小的内存
	int* p = (int*)malloc(1 * sizeof(int));	
	*p = 10;
	printf("*p = %d\n", *p);
/*
int*p 指针p指向申请的内存
(int*)  类型强转,原函数返回值是void*,所以需要强转
1 * sizeof(int) 需要申请的字节数

*/
    
    
    
	// 2 使用malloc申请八个int类型大小的内存
	int* p1 = (int*)malloc(8 * sizeof(int));
	*p1 = 66;
	printf("*p1 = %d\n", *p1);
	*(p1 + 1) = 77;
	printf("*(p1 + 1) = %d\n", *(p1 + 1));
	p1[2] = 88;
	printf("p1[2] = %d\n", p1[2]);

	for (int i = 0; i < 8; i++)
	{
		p1[i] = i;
	}
	for (size_t i = 0; i < 8; i++)
	{
		printf("%3d", *(p1 + i));
	}

	return 0;
}


*(p+n)=str[n]

二) calloc

#include <stdio.h>
#include <stdlib.h>

int main()
{
	// calloc
	// 函数原型: void * __cdecl calloc(_In_ _CRT_GUARDOVERFLOW size_t _Count, _In_ _CRT_GUARDOVERFLOW size_t _Size);
	// 返回:void*	参数: 申请的内存个数,单个内存的大小
	// 1 使用calloc申请一个int类型大小的内存
	int* p = (int*)calloc(1, sizeof(int));

	// 2 使用calloc申10个int类型大小的内存
	int* p1 = (int*)calloc(10, sizeof(int));
	for (size_t i = 0; i < 10; i++)
	{
		p1[i] = i * 10;
	}
	for (size_t i = 0; i < 10; i++)
	{
		printf("%3d", *(p1 + i));
	}

	return 0;
}

三)free

#include <stdio.h>
#include <stdlib.h>

int main()
{
	// calloc
	// 函数原型: void * __cdecl calloc(_In_ _CRT_GUARDOVERFLOW size_t _Count, _In_ _CRT_GUARDOVERFLOW size_t _Size);
	// 返回:void*	参数: 申请的内存个数,单个内存的大小
	// 1 使用calloc申请一个int类型大小的内存
	int* p = (int*)calloc(1, sizeof(int));
	free(p);		// 释放申请的内存
	p = NULL;		// 指针置空


	// 2 使用calloc申10个int类型大小的内存
	int* p1 = (int*)calloc(10, sizeof(int));
	for (size_t i = 0; i < 10; i++)
	{
		p1[i] = i * 10;
	}
	for (size_t i = 0; i < 10; i++)
	{
		printf("%3d", *(p1 + i));
	}C
	free(p1);		// 释放一段内存
	p1 = NULL;		// 指针置空

	return 0;
}

思考:

连续申请一段内存,类似于一维数组,那么二维数组呢?

#include <stdio.h>
#include <stdlib.h>

int main()
{
	/*int* p0 = (int*)calloc(5, sizeof(int));
	int* p1 = (int*)calloc(5, sizeof(int));
	int* p2 = (int*)calloc(5, sizeof(int));

	int** pp = (int**)calloc(3, sizeof(int*));
	pp[0] = p0;
	pp[1] = p1;
	pp[2] = p2;

	for (size_t i = 0; i < 3; i++)
	{
		for (size_t j = 0; j < 5; j++)
		{
			printf("%2d", pp[i][j]);
		}
		printf("\n");
	}*/

	int lin, row;
	printf("input line and row:");
	scanf("%d %d", &lin, &row);

	// 申请
	int** pp = (int**)calloc(lin, sizeof(int*));
	for (size_t i = 0; i < lin; i++)
	{
		pp[i] = (int*)calloc(row, sizeof(int));
	}

	// 使用
	for (size_t i = 0; i < lin; i++)
	{
		for (size_t j = 0; j < row; j++)
		{
			printf("%2d", pp[i][j]);
		}
		printf("\n");
	}

	// 释放
	for (int i = 0; i < lin; i++)
	{
		free(pp[i]);
		pp[i] = NULL;		// 可以省略
	}
	free(pp);
	pp = NULL;
	return 0;
}

注意:

二维数组的内存是连续的 但是 像这样申请的内存 不一定连续(%99申请的列不连续,极小可能申请的列连续)
1.如果 malloc之后没有及时的free,则内存泄漏(memory leak)
 malloc的内存free一次就可以,重复free也是未定义行为

 如果申请了两次malloc但是free一次情况:
   第一次申请的内存使用p进行了保存,接下来修改p的内存后,在也没法找到第一次申请内存的地址,有就无从释放。

  2.free只是将内存释放,并不会设为NULL, //此刻应该手动将str设为NULL,这样才不会存在隐患

作业:

动态数组(自动扩容)

扩容规则:变为原来的两倍

三 指针与函数

四 指针小结

一)使用指针的注意事项

二)指针与引用对比

	int a = 10;
	int& b = a;
	printf("b = %d", b);
	// 引用:给变量取别名
typedef _W64 unsigned int   size_t; 给类型取别名
posted @ 2020-11-11 23:23  鹤儿哈  阅读(107)  评论(0)    收藏  举报