“第三十二天” 动态内存管理 malloc,calloc,realloc

        动态内存管理

局部变量和形式参数都放在栈区,malloc,calloc,realloc开辟的动态内存在堆区。

变长数组的意思不是可以变长,而是可以用变量定义大小;

        malloc和free

void* malloc(size_t size);
//size 参数是你要分配的内存块的字节数。
//malloc 返回一个指向新分配内存的指针。注意,返回的指针的类型是 void*,因此通常需要进行类型转换。

这个函数向内存申请一块连续可用的空间,并返回指向这块空间的指针。

如果开辟成功,则返回一个指向开辟好空间的指针;

如果开辟失败,则返回一个NULL指针,因此malloc的返回值一定要做检查;

返回值的类型是 void* ,因为malloc函数不知道开辟空间的类型,在使用的时候注意要类型转化;

如果参数size是 0 ,malloc的行为标准是未定义的,结果取决于编译器。

        注意开辟内存时的单位是字节

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

void free (void* ptr);

        free函数用来释放动态开辟的内存。

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

如果参数ptr是NULL指针,那么函数什么都不做;

申请内存过大导致申请失败,此时malloc函数返回值是NULL;

        calloc

void* calloc (size_t num,size_t size);
//num_elements 参数是你要分配的元素数量。
//element_size 参数是每个元素的大小(以字节为单位)。
//calloc 返回一个指向新分配内存的指针。
//与 malloc 一样,返回的指针的类型是 void*,通常需要进行类型转换。

函数的功能是num个大小为size的元素开辟一块空间,并且把空间的每个字节初始化为 0

与函数malloc的区别只在于calloc会返回地址之前把申请的空间的每个字节初始化为0,同时可以定义申请元素的大小(类型)和个数;

——————————————————————————————————————————

上面两个函数好像只是能申请动态内存(在堆区开辟空间),但实际上好像也不能改变内存大小

        realloc:realloc函数可以做到对动态开辟内存大小的调整;

void* realloc(void* ptr,size_t size);
//ptr是要调整的内存地址
//size为调整后的大小;
//返回值为调整之后的内存起始位置

注意 size 为调整后内存的大小,不是要在增加多少。

realloc 将尝试将现有内存块的大小更改为新的大小。它有以下几种情况:

  • 如果新的大小大于现有大小,它将尝试扩展内存块。
  • 如果新的大小小于现有大小,它将尝试缩小内存块。
  • 如果无法调整大小,它将返回 NULL 并保持原始内存块不变。

释放内存:如果 realloc 成功地调整了内存大小,你无需调用 free 来释放旧的内存块,因为 realloc 已经将旧内存块的内容复制到了新内存块中。你只需要更新指针以指向新的内存块。

处理失败情况:如果 realloc 返回 NULL,表示内存调整失败,你应该处理这种情况,否则可能会导致内存泄漏。一种处理方式是释放旧的内存块,并进行适当的错误处理。

不要使用未初始化的内存:如果 realloc 扩展了内存块,新分配的部分可能未初始化,因此需要小心使用。你可以使用适当的初始化方法来确保新内存的内容是已知的。

指针的值可能会改变:在调用 realloc 后,指向内存块的指针的值可能会发生变化,因此你应该使用返回的指针来更新你的引用。

如果给realloc传递的地址是NULL,表示没有现有的内存块可供调整。

        对于 malloc ,calloc,realloc三个函数,不要忘记使用时,对返回值的判断是否为空指针,以及使用之后不要忘记内存的释放,和指针赋值为空

//错误使用

//1、对NULL指针的解引用操作
void test()
{
    int* p = (int*)malloc(INT_MAX);
    *p = 20;
    //如果内存开辟失败,此时p的值是NULL,会出问题
    free(p);
}

//2、对动态开辟空间的越界访问
void test()
{
    int i = 0;
    int* p = (int*)malloc(10 * sizeof(int));
    //这里其实还打开了定义申请内存大小的思路 ,sizeof的运用
    if (NULL == p)
    {
     exit(EXIT_FAILURE);
    }
    for (i = 0; i <= 10; i++)
    {
        *(p + i) = i;//当 i 为 10的时候越界访问。
    }
    free(p);
    p = NULL;
    return 0;
}

//3、对非动态开辟内存使用free释放
void test()
{
    int a = 10;
    int*p = &a;
    free(p);//err
}

//4、使用free释放一块动态内存的一部分
void test()
{
    int i = 0;
    int* p = (int*)malloc(10 * sizeof(int));
    if (NULL == p)
    {
     exit(EXIT_FAILURE);
    }
    for (i = 0; i < 10; i++)
    {
        *(p++) = i;//p的值一直在加,指向的地址往前移动
    }
    free(p);
    p = NULL;
    return 0;
}

5、//对同一块内存多次释放
void test()
{
    int* p = (int*)malloc(10 * sizeof(int));
    //...
    free(p);
    free(p);
    //...
}

//6、动态开辟内存忘记释放(内存泄露)
int test()
{
    int* p = (int*)malloc(10 * sizeof(int));
    if (NULL == p)
    {
        printf("%s", strerror(errno));
        return 1;
    }
    return 0;
}
//这里退出函数之后,p会销毁,但开辟内存并没有释放,但由于p的销毁,这部分内存没有办法再找到
//忘记释放不再使用的动态开辟的空间会造成内存泄漏
//练习:最大公约数,最小公倍数之和
//1、正常
int math1(int x, int y)
{
	int i = 0;
	int min = x < y ? x : y;
	int max = x > y ? x : y;
	for (i = min; i > 1; i--)
	{
		if (!(x % i || y % i))
			break;
	}
	min = i;
	for (i = max;; i++)
	{
		if (!(i % x || i % y))
			break;
	}
	max = i;
	return min + max;
}

//2、老师 辗转相除法
int math2(int x, int y)
{
	int i = x;
	int j = y;
	int r = 0;
	while (r = i % j)
		//用 i%j的余数作为判断循环的标准,如果可以除尽,就直接退出循环了
		//而且这里是没有判断x,y的大小,直接赋值的,在求余的时候先默认 i 是较大值,如果错误也就是多循环一次,然后就之后就是大除以小
		//这里实际上 j 代表的是最大公约数,r用来存储余数
	{
		i = j;
		j = r;
	}
	return j + x * y / j;
}
int main()
{
	int x, y;
	scanf("%d %d", &x, &y);
	int ret = math1(x, y);
	printf("%d\n", ret);
	ret = math2(x, y);
	printf("%d\n", ret);
	return 0;
}
//练习:空心正方形图案
void print(int n)
{
	int i = 0;
	for (i = 0; i < n; i++)
	{
		int j = 0;
		for (j = 0; j < n; j++)
		{
			if (0 == i || 0 == j || n - 1 == i || n - 1 == j)
				printf("* ");
			else
				printf("  ");
		}
		printf("\n");
	}
}

int main()
{
	int n = 0;
	scanf("%d", &n);
	print(n);
	return 0;
}

        柔性数组

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

typedf struct st_type
{
    int i;
    int a[0];//柔性数组成员
}type_a;
//如果编译器报错无法编译可以改成:
typedef struct st_type
{
    int i;
    int a[];//柔性数组成员
}type_a;

        柔性数组的特点:

结构体的柔性数组成员前面必须至少有一个其他成员;

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

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

即使在申请过内存之后,定义的变量的结构大小仍不包括柔性数组。

        

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值