【C语言12】动态内存分配

目录

1、当前我们知道的内存使用方式

2、为什么存在动态开辟空间

3、malloc和free

4、calloc函数

5、relloc函数

6、动态内存开辟的常见错误

7、C/C++程序的内存开辟

8、柔性数组


1、当前我们知道的内存使用方式

1.创建一个变量

int a = 10; //局部变量 - 栈区

int g_a = 10; //全局变量 - 静态区

栈区:局部变量、函数的形参、返回数据、返回地址等(临时)

自动被释放

堆区:动态内存分配

一般由程序员分配释放

静态区:全局变量、静态变量(static)

由系统释放

2.创建一个数组

局部范围内的数组在栈区

全局范围内的数组在静态区


2、为什么存在动态开辟空间

对于空间的需求,不仅仅是上述的情况。

有时候我们需要的空间大小在程序运行的时候才能知道, 那数组的编译时开辟空间的方式就不能满足了。

这时候就只能试试动态存开辟了。

动态开辟空间在堆区。


3、malloc和free

1.C语言提供了一个动态内存开辟的函数:void* malloc (size_t size);

void* - 由用户来决定返回的是什么类型

size_t - 无符号整型

size - 开辟多少个字节

举例:

int* p = (int*)malooc( 10*sizef(int) );//10个整型的字节大小

malooc函数会返回一个指向开辟好空间的指针,或者是NULL指针,因此malloc的返回值一定要做检查。

2.C语言提供了另外一个函数free,专门是用来做动态内存的释放和回收的。

void free (void* ptr);

void* ptr - 开辟空间的数据块的首地址

注意:free之后,ptr的地址还在,我们要把地址ptr赋值为空指针,以避免适应使用ptr找到这个地址。

如果参数 ptr 指向的空间不是动态开辟的,那free函数的行为是未定义的。

如果参数 ptr 是NULL指针,则函数什么事都不做。


4、calloc函数

1.calloc 函数也用来动态内存分配:void* calloc (size_t num, size_t size);

num - 元素的个数

size - 元素的长度(字节)

举例:

int *p = (int*)calloc(10, sizeof(int));

与函数 malloc 的区别:只在于 calloc 会在返回地址之前,把申请的空间的每个字节初始化为全0。


5、relloc函数

1. realloc 函数就可以做到对动态开辟内存大小的调整:void* realloc (void* ptr, size_t size);

ptr - 要调整的内存地址

size - 调整之后新大小

返回值 - 调整之后的内存起始位置

举例:

ptr = (int*)realloc(ptr, 1000);

realloc函数在调整原内存空间大小的基础上,还会将原来内存中的数据移动到新的空间。

注意:

realloc在调整内存空间的是存在两种情况:

a.原有空间之后有足够大的空间 - 追加之后,再返回起始地址

b.原有空间之后没有足够大的空间 - 一次性开辟一片足够的空间,然后将旧的数据拿下来,放在新的开辟的空间。在这种情况下,ptr的地址将发生改变。还可能将原数据弄丢。所以要用一个新的变量来接收返回值。

 if(p != NULL)
 {
 ptr = p;
 }

先使用malloc函数,后使用realloc函数。

先释放realloc函数开辟的空间,后释放malloc函数开辟的空间,

举例:

int main()
{
 int *ptr = (int*)malloc(100);
 if(ptr != NULL)
 {
     //业务处理
 }
 else
 {
     exit(EXIT_FAILURE);    
 }
 //扩展容量
 //代码1
 ptr = (int*)realloc(ptr, 1000);//可能导致数据丢失
 
 //代码2
 int*p = NULL;
 p = realloc(ptr, 1000);
 if(p != NULL)
 {
 ptr = p;
 }
 //业务处理
 free(ptr);
 return 0;
}

6、动态内存开辟的常见错误

1.对NULL指针的解引用的操作

如果在开辟空间的时候,没有开辟成功,这时在该内存存储数据的时候,就会产生NULL指针,导致非法访问内存空间。

2.对动态开辟空间越界访问

如果开辟空间小于我们的数据要存储的空间,会导致越界访问。

3.使用free释放非动态开辟空间

使用free释放非动态开辟空间后,程序会卡死。

4.使用free释放动态内存中的一部分

指向动内存空间起始地址发生了变化,使在释放的时候,只释放了一部分。并且有一个巨大的风险,起始位置找不到之后,可能永远也找不到这块空间了。

5.对同一块动态内存空间多次释放

也会卡死。

6.动态开辟空间忘记释放

忘记释放后,想释放的时候已经找不到了,导致内存泄漏。

注意:

动态开辟的空间,两种回收方式:

1.主动free

2.程序结束

切记: 动态开辟的空间一定要释放,并且正确释放 。


7、C/C++程序的内存开辟

 图源比特

栈区:局部变量、函数的形参、返回数据、返回地址等(临时)

自动被释放

堆区:动态内存分配

一般由程序员分配释放

静态区:全局变量、静态变量(static)

由系统释放


8、柔性数组

1、什么是柔性数组?

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

举例:

typedef struct st_type
{
 int i;
 int a[0];//大小未知 - 柔性数组成员

//有些编译器会报错无法编译可以改成:int a[];

}type_a;

2、特点

1.结构中的柔性数组成员前面必须至少一个其他成员

2.sizeof 返回的这种结构大小不包括柔性数组的内存。

举例:

typedef struct st_type
{
 int i;
 int a[0];//柔性数组成员
}type_a;
printf("%d\n", sizeof(type_a));//输出的是4

3、包含柔性数组成员的结构,用malloc ()函数进行内存的动态分配,并且分配的内存应该大于结构的大小,以适应柔性数组的预期大小。

举例:

type_a *p = (type_a*)malloc(sizeof(type_a)+100*sizeof(int));

使用malloc函数的次数多了,会影响效率,并且增加内存碎片。

4、优势

方便内存释放

连续的内存有益于提高访问速度,也有益于减少内存碎片

柔性数组扩展阅读

猜你喜欢

转载自blog.csdn.net/m0_64432537/article/details/122980514