动态内存管理

动态内存函数

 free

动态内存释放函数,在指针释放内存之后,指针依旧指向动态内存块的首地址,所以要把NULL赋给指针,避免出现错误(野指针)。

如果prt指向的空间是非动态开辟的,则free操作时未被定义的

如果prt指针为null,则free函数什么都不做 

malloc

开辟size个大小的内存块,单位为字节。

动态开辟函数,函数向内存申请一块连续的空间,成功则返回这片空间的指针,开辟失败则返回NULL指针,所以需要对malloc的返回值进行判断。

#include <stdio.h>
#include <stdlib.h>
int main()
{
    int num = 0;
    scanf("%d", &num);
    int* ptr = NULL;
    ptr = (int*)malloc(num*sizeof(int));
    if(NULL != ptr)
     {
         int i = 0;
         for(i=0; i<num; i++)
         {
             *(ptr+i) = 0;
         }
     }
     free(ptr);
     ptr = NULL;
     return 0;
}

注意:malloc返回值类型是void指针类型,在使用时要强转。 

calloc

动态内存开辟函数,为num个大小为size的元素开辟空间,并且把空间的每个字节都初始化为0。

realloc

动态内存管理,prt时要调整开辟空间内存大小的地址,size是调整后的内存开辟的大小。

#include <stdio.h>
#include <stdlib.h>
int main()
{
	int* ptr = (int*)malloc(100);
	if (ptr != NULL)
	{
	}
	else
	{
		exit(EXIT_FAILURE);
	}
	int* p = NULL;
	p = realloc(ptr, 1000);
	if (p != NULL)
	{
		ptr = p;
	}
	free(ptr);
	return 0;
}

注意:realloc可能当空间不够时,会返回一个空值,所以需要判断realloc有没有成功开辟新空间。

常见的动态内存错误

对null指针的解引用操作

void test()
{
	int* p = (int*)malloc(INT_MAX / 4);
	*p = 20;
	free(p);
}

上面的代码没有对指针p进行判断,如果malloc开辟空间失败则会返回null,而后面的操作可能会对null进行解引用操作。

对动态开辟空间的越界访问

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) = i;
	}
	free(p);
}

上面的代码中当i=10时,存在越界访问。

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

void test()
{
	int a = 10;
	int* p = &a;
	free(p);
}

free释放非动态内存操作存在问题,局部变量a被开辟在栈区,动态内存开辟则是在堆区。

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

void test()
{
	int* p = (int*)malloc(100);
	p++;
	free(p);
}

指针p的位置发生变化没有指向动态内存的初始位置。

对同一块动态内存多次释放

void test()
{
	int* p = (int*)malloc(100);
	free(p);
	free(p);
}

第一次free操作后没有把null赋给p,会出现p依旧指向某个地址但是这个地址指向的内存块已经被free掉了,使得p成为野指针,第二次操作就是在对野指针的解引用操作。 

动态内存空间忘记释放即内存泄漏

void test()
{
	int* p = (int*)malloc(100);
	if (NULL != p)
	{
		*p = 20;
	}
}
int main()
{
	test();
	while (1);
}

忘记释放不再使用的动态开辟的空间则会造成内存泄漏。

 动态内存的笔试题

运行Test函数会有什么结果?

void GetMemory(char* p)
{
	p = (char*)malloc(100);
}
void Test(void)
{
	char* str = NULL;
	GetMemory(str);
	strcpy(str, "hello world");
	printf(str);
}

程序崩溃

崩溃的主要原因是因为对空指针的解引用操作,在GetMemory函数中,str将null赋给形参p,然后开辟了一个100个字节的内存块将首地址赋给了p,然后函数返回void,函数并没有改变str,所以在strcpy中str依旧是空指针。

第二个明显的错误就是存在内存泄漏,并且在退出GetMemory函数后指针p也随之消失,无法在查询到动态开辟的内存块首地址了,也就无法在释放内存了。

char* GetMemory(void)
{
	char p[] = "hello world";
	return p;
}
void Test(void)
{
	char* str = NULL;
	str = GetMemory();
	printf(str);
}

打印乱码

在GetMemory函数中,在栈区开辟了一个临时的数组p,函数返回了p的首地址但是数组里面的内容随着函数的结束而被销毁,所以函数返回了p的地址但是p里面的内容可能被篡改了所以打印了乱码。

void GetMemory(char** p, int num)
{
	*p = (char*)malloc(num);
}
void Test(void)
{
	char* str = NULL;
	GetMemory(&str, 100);
	strcpy(str, "hello");
	printf(str);
}

 能正常打印,但是存在内存泄漏

开辟了一个动态内存的空间但是没有释放。

void Test(void)
{
	char* str = (char*)malloc(100);
	strcpy(str, "hello");
	free(str);
	if (str != NULL)
	{
		strcpy(str, "world");
		printf(str);
	}
}

程序崩溃

在对动态内存块释放后,str依旧指向动态内存块的首地址,在if语句的判断中始终为真,所以进入语句,但是该地址指向的内存块已经不存在,str变为野指针,在strcpy中存在对野指针的解引用操作,导致程序崩溃。

程序的内存开辟

程序分配的几个区域

1、栈区:主要存放局部变量和函数参数等,其特点就是在栈区创造的变量出了作用域就会被销毁。

2、堆区:主要存放动态内存,需要程序员主动释放。

3、静态区(数据段):主要存放全局变量和静态数据,在程序结束后由系统释放。

4、代码段:主要存放类成员函数和全局函数。

柔性数组

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

举例

typedef struct st_type
{
	int i;
	int a[];//柔性数组成员
}type_a;

特点

结构中的柔性数组成员前面必须至少一个其他成员。
sizeof 返回的这种结构大小不包括柔性数组的内存。


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

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
struct s
{
	char j;	
	int i;
	int a[];
}S;
int main()
{
	struct s* p = (struct s*)malloc(sizeof(S) + 12);//给柔性数组成员a开辟了12个字节空间
	if (p == NULL)
	{
		perror("malloc");
		return 1;
	}
	p->j = 'a';
	p->i = 64;
	for (int i = 0; i < 3; i++)
	{
		p->a[i] = i;
	}
	for (int i = 0; i < 3; i++)
	{
		printf("%d", p->a[i]);
	}
	//扩容
	struct s* ps = (struct s*)realloc(p, sizeof(S) + 32);
	if (ps != NULL)
	{
		p = ps;
	}
	else
	{
		perror("realloc");
		return 1;
	}
	//释放
	free(p);
	p = NULL;
	return 0;
}

扩容前的柔性数组在内存中。 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值