C语言动态内存开辟详解(malloc,calloc,realloc,free,柔型数组)

C语言动态内存开辟详解(malloc,calloc,realloc,free,柔型数组)_第1张图片

目录

  • 一、概述
  • 二、相关库函数的使用
    • 1.malloc
    • 2.calloc
      • malloc vs. calloc 异同
    • 3.free的使用
    • 4.realloc
  • 三、易错点
  • 四、C\C++程序的内存开辟规则
  • 五、柔性数组

一、概述

Q:为什么需要动态内存分配?
A:使用这两种方式开辟内存的大小是固定的:

int val = 20;   //在栈上开辟4个字节的空间
char arr[10] = {0};   //在栈上开辟10个字节的连续空间

另外,数组在申明的时候,必须指定数组的长度,它所需要的内存在编译时分配。
但是,当所需空间的大小在程序运行时才能知道,也就无法事先开辟一块空间了,这时候就需要动态内存开辟。


二、相关库函数的使用

注:以下均需包含

1.malloc

void* malloc (size_t size);

使用:

#include 
#include 
int main()
{
	int* ptr = (int*)malloc(40);
	if (ptr == NULL)     //务必判断是否开辟成功,否则退出并报错
	{
		perror("malloc"); 
		//或printf("%s",strerror(errno));  
		//strerror头文件是   errno头文件是
		//直接打印用perror,如果只需要获得错误码不需要打印就使用strerror(errno)
		return 1;
	}
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", *(ptr + i));
	}
	//必不可少的使用后free 和 NULL置空
	free(ptr);
	ptr = NULL;
	return 0;
}

在这里插入图片描述
易错:1.忘记判断NULL 2.忘记free和指针置空
free只是释放(返还)空间,但不会将其置为空指针,为了防止它是野指针、防止内存泄漏,需要手动置为空指针
注:free不释放等程序结束也能自动回收内存空间


2.calloc

void* calloc (size_t num, size_t size);

使用:


#include 
#include 
int main()
{
	int* ptr = calloc(10, 4);
	if (ptr == NULL)
	{
		printf("%s", strerror(errno));
		return 1;
	}
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", *(ptr + i));
	}
	free(ptr);
	ptr = NULL;
	return 0;
}

在这里插入图片描述


malloc vs. calloc 异同

1.相同之处:

1.无符号整形(size_t)是要开辟内存的大小,以字节为单位
2.返回值void*表示适用于所有类型的指针来接收,并且需要强制类型转换来适应所要接受的类型指针
3.如果开辟成功,则返回一个指向开辟好空间的指针。
   如果开辟失败,则返回一个NULL指针,因此对malloc、calloc以及realloc的返回值一定要做检查
   (例如设置一个指针来存放返回的地址,判断是否为NULL,是NULL返回错误信息
   如果不为NULL,再使用要使用的指针来存放,这样可以避免本身要使用的指针值改变)。
4.如果参数 size 为0,标准是未定义的,取决于编译器。

2.不同点

1.malloc开辟的空间是未初始化的,calloc会默认初始化为0,单位为字节
2.calloc中num参数表示要开辟num个size大小的字节,
比如calloc(3,4); 表示开辟3个大小为4个字节的内存,可以使用在数组中
3.相比于malloc,calloc的初始化更精确可控一些,建议使用calloc

3.free的使用

void free (void* ptr);

使用malloc等动态内存开辟相关库函数时一定配合free使用,先free()后把指针置空,避免内存泄漏和野指针。


4.realloc

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

1.ptr 是要调整的内存地址
2.size 调整之后新大小
3.返回值为调整之后的内存起始位置。
使用:


#include 
#include 
int main()
{
	int* p = (int*)calloc(10, sizeof(int));
 
	if (p == NULL)
	{
		perror("calloc");
		return 1;
	}
 
	//realloc从新分配大小为20个字节
	int* ptr = (int*)realloc(p, 20 * sizeof(int));
	if (ptr != NULL)
	{
		p = ptr;
	}
 
	//free释放
	free(p);
	p = NULL;
	return 0;
}

总结:realloc开辟空间,后面空间足够时还是用原首地址,扩容(直接追加);若空间不足,则会开辟一块新的空间,(一块连续的空间),把原来的内容拷贝到新的地址内,此时地址改变。如果找不到合适的空间来调整大小,则返回空指针。所以一般不用原来的指针变量来接收值,防止值被改变。


三、易错点

C语言动态内存开辟详解(malloc,calloc,realloc,free,柔型数组)_第2张图片


四、C\C++程序的内存开辟规则

C语言动态内存开辟详解(malloc,calloc,realloc,free,柔型数组)_第3张图片
C/C++程序内存分配的几个区域

  1. 栈区(stack):在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。 栈区主要存放运行函数而分配的局部变量、函数参数、返回数据、返回地址等。
  2. 堆区(heap):一般由程序员分配释放, 若程序员不释放,程序结束时可能由系统自动回收 。
  3. 数据段(静态区)(static)存放全局变量、静态数据。程序结束后由系统释放。
  4. 代码段:存放函数体(类成员函数和全局函数)的二进制代码。

实际上,普通的局部变量是在栈区分配空间的,栈区的特点是在上面创建的变量出了作用域就销毁。
但被static修饰的变量则存放在数据段(静态区),数据段的特点是在上面创建的变量,直到程序结束才销毁,所以生命周期变长了。


五、柔性数组

1.定义:在C99 中,结构中的最后一个元素允许是未知大小的数组,这就叫做『柔性数组』成员。

typedef struct st_type
{
 int i;
 int a[0];//柔性数组成员    (若无法编译,可改为int a[];)
}type_a;

2.特点:

  • 结构中的柔性数组成员前面必须有至少一个其他成员。
  • sizeof 返回的这种结构大小不包括柔性数组的内存。
  • 包含柔性数组成员的结构用malloc ()函数进行内存的动态分配,并且分配的内存应该大于结构的大小,以适应柔性数组的预期大小。

3.使用:

//代码1
int i = 0;
type_a *p = (type_a*)malloc(sizeof(type_a)+100*sizeof(int));
//业务处理
p->i = 100;
for(i=0; i<100; i++)
{
 p->a[i] = i;
}
free(p);

注意:对有柔型数组的结构体动态开辟时,要用sizeof() + —(100 * sizeof(int))的形式来分配大小
4.优点
使用柔型数组的好处是方便内存释放,一次性分配内存,例如用指针动态开辟时可能忘记释放两次,而用柔型数组做一次free就可以把所有的内存释放掉。
其次,有利于提高访问速度,因为是内存的开辟是连续的,也有益于减少内存碎片(没有被使用的一些内存)。


※推荐阅读※
C语言结构体里的成员数组和指针

你可能感兴趣的:(c语言,c++)