动态内存管理(柔性数组)

动态内存管理

文章目录

  • 动态内存管理
      • malloc
      • calloc
      • realloc
      • free
      • 柔性数组


1.我们如果想获得一块可大可小,空间不够了可以增加,空间大了可以缩小
应该如何实现呐?

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

这样只能获得固定的空间,程序一跑空间就改变不了。
2.c语言为我们提供了一组库函数来实现这一目标:

  • malloc
  • calloc
  • realloc
  • free(用来释放开辟的空间)

关于malloc是如何申请内存的,推荐看这篇文章,写的超级棒
自己动手实现一个malloc内存分配器 - 码农的荒岛求生的文章 - 知乎
https://zhuanlan.zhihu.com/p/367060283

malloc

动态内存管理(柔性数组)_第1张图片

  • 返回值malloc返回一个指向已分配空间的void指针,如果可用内存不足则返回NULL。
  • 若要返回指向非void类型的指针,请对返回值使用类型强制转换。
  • 参数是size_t单位是字节
  • 如果参数为0这种行为,c语言未定义,尽量不要这么写
  • 动态内存管理(柔性数组)_第2张图片

此时tmp指向一块开辟好的40个字节的空间

  • 释放:由于malloc开辟的空间是在栈上的,所以只能程序员自己释放,否则只能程序结束后由操作系统释放(但是有些程序有可能是没日没夜的运行,不释放的话有可能会造成内存泄漏。)
    完整示范:
#include 
#include 
int main()
{
	int *tmp=(int *)malloc(40);
	//malloc有可能开辟失败,使用前一定要进行判断
	//不能直接使用tmp,tmp可能是空指针,对空指针解引用会出现错误
	if (tmp == NULL)
	{
		printf("%s", strerror(errno));
		return 0;
	}
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		*(tmp + i) = i;
		printf("%d ", *(tmp + i));
	}
	//释放
	free(tmp);
	tmp = NULL;
	return 0;
}

calloc

动态内存管理(柔性数组)_第3张图片

  • 与函数 malloc 的区别只在于 calloc 会在返回地址之前把申请的空间的每个字节初始化为全0。
  • 函数的功能是为 num 个大小为 size 的元素开辟一块空间,并且把空间的每个字节初始化为0。

realloc

动态内存管理(柔性数组)_第4张图片
realloc和前面的有点不一样

  • 第一个参数必须是malloc\calloc出来的(必须是动态内存开辟出来的指针)即要调整的内存的地址
  • size_t调整之后的新大小
  • 情况一:内存看做一条长长的停车场,我们申请内存就是要找到一块停车位,释放内存就是把车开走让出停车位。只不过这个停车场比较特殊,我们不止可以停小汽车、也可以停占地面积很小的自行车以及占地面积很大的卡车。要扩展内存就直接原有内存之后直接追加空间,原来空间的数据不发生变化。
    动态内存管理(柔性数组)_第5张图片
  • 情况2:原有空间之后没有足够多的空间时,扩展的方法是:在堆空间上另找一个合适大小的连续空间来使用。并且释放旧的空间,这样函数返回的是一个新的内存地址。

因此使用realloc时候一定注意是否原空间地址改变

#include 
int main()
{
	int *tmp=(int *)malloc(40);
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		*(tmp + i) = i;
		printf("%d ", *(tmp + i));
	}
	int* pp=(int *)realloc(tmp, 80);
	//此时不能直接写成
	//tmp = pp;
	//万一realloc开辟失败返回一个空指针,那么原来的
	//40个字节都找不到了
	if (pp != NULL)
	{
		tmp = pp;
		//使用
		for (i = 0; i < 20; i++)
		{
			*(tmp + i) = i;
			printf("%d ", *(tmp + i));
		}
	}
	//释放
	free(pp);
	pp = NULL;
	return 0;
}

realloc也可以开辟一块空间,如

#include 
#include 
int main()
{
	int *tmp= (int*)realloc(NULL, 40);
	//这里也可以开辟一块空间
	//效果和malloc一摸一样
	if (tmp == NULL)
	{
		printf("%s", strerror(errno));
		return 0;
	}
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		*(tmp + i) = i;
		printf("%d ", *(tmp + i));
	}
	free(tmp);
	tmp = NULL;
	return 0;
}

free

动态内存管理(柔性数组)_第6张图片
这里参数是开辟的起始位置的地址
注意以下错误:

1.使用free释放一块动态开辟内存的一部分

//使用free释放一块动态开辟内存的一部分
int main()
int* p = (int* )malloc(40);
if (p == NULL)
printf("%s\n"strerror(errno));
return 0;
}
int i=0;
//[1] [2] [3] [4] [5] [][] [][][]
for(i=0;i<5;i++)
{
*p=i+1;
p++;|
}
//释放
free(p);
p = NULL;
return 0;
}

最好不要让指向动态开辟的空间的起始位置的地址跑来跑去,否则释放不了,free只能释放起始位置的地址。想释放这块空间必须提供起始位置的地址

2 .对非动态开辟内存使用free释放

int main()
int arr[10] = { 1,2,3,4,5 };
int* p = arr;
//....
free(p);
p = NULL;
return 0;

这里p开辟的空间不在堆上不能释放

柔性数组

  1. C99 中,结构中的最后一个元素允许是未知大小的数组,这就叫做『柔性数组』成员。
  2. 结构中的柔性数组成员前面必须至少一个其他成员。
  3. sizeof 返回的这种结构大小不包括柔性数组的内存。
  4. 包含柔性数组成员的结构用malloc ()函数进行内存的动态分配,并且分配的内存应该大于结构的大小,以适应柔性数组的预期大小。
#include 
#include 
struct S
{
    int n;
    char C;
    int arr[0];//柔性数组成员
};
int main()
{
    //           8 +                  40
    struct S* ps = (struct S*)malloc(sizeof(struct S) + 10 * sizeof(int));
   if (ps == NULL)
    {
         printf("%s\n",strerror(errno));
         return 1;
    }
    //使用
 ps->n = 100;
 ps->C = 'w';
 int i = 0;
 for (i = 0; i < 10; i++)
  {
      ps->arr[i] = i;
  }
for (i = 0; i < 10; i++)
{
    printf("%d\n", ps->arr[i]);
}
//调整arr数组的大小
struct S* ptr = (struct S*)realloc(ps,sizeof(struct S) + 20 * sizeof(int));
if (ptr == NULL)
{
     printf("%s\n",strerror(errno));
     return 1;
}
else
     ps = ptr;

//使用
//释放
free(ps);
ps = NULL;

}

可以动态的管理一块数组空间,个人感觉这个很有用

你可能感兴趣的:(c++,c语言,柔性数组)