动态内存分配(使用时要包含stdlib.h)

1. 动态内存函数—malloc

void malloc(size_t size);

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

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

②开辟空间失败,则返回一个NULL指针,因此malloc的返回值一定要做检查

③返回值的类型是void*,所以malloc函数并不知道开辟空间放的数据的类型,具体在使用的时候使用者自己决定

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

malloc申请的内存空间,当程序退出时还给操作系统,当程序不退出,申请的动态空间不会主动释放,因此就需要释放空间的函数free

2. 释放动态内存函数—free

void free (void* ptr);

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

int main()
{
	int a = 10;
	int* p = &a;
	free(p);//错误
	return 0;

}

②如果参数ptr是NULL,函数什么都不做。

使用malloc和free函数的例子

#include<stdio.h>
#include<stdlib.h>//动态内存函数的使用要包含头文件
int main()
{
	int num = 0;
	scanf("%d", &num);
	int* ptr = NULL;
	ptr = (int*)malloc(num * sizeof(int));//开辟num个大小为int类型的空间
	if (NULL == ptr)//判断开辟动态内存是否成功,不成功打印错误原因
	{
		perror("malloc");
		return 1;//开辟失败,返回1,结束程序,与下面返回0区别开,
	}
	int i = 0;
	for (i = 0; i < num; i++)
	{
		printf("%d\n", *(ptr + i));//malloc函数开辟空间后,直接返回指向该空间的指针,不会初始化空间的内容,因此打印出来是随机值
	}
	for (i = 0; i < num; i++)
	{
		*(ptr + i) = i;//赋值
		printf("%d ", *(ptr + i));//打印
	}
	free(ptr);//释放空间
	ptr = NULL;//将ptr置为空指针,不然ptr就变成野指针了
	return 0;
}

3. 动态内存函数—calloc

void * calloc(size_t num, size_t size);

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

例:

#include<stdio.h>
#include<stdlib.h>
int main()
{
	int* ptr = NULL;
	ptr = (int*)calloc(10, sizeof(int));
	if (NULL == ptr)
	{
		perror("calloc");
		return 1;
	}
	free(ptr);
	ptr = NULL;
	return 0;
}

4. 动态内存调整函数—realloc

realloc函数用于对动态开辟空间大小的调整

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

①ptr是要调整的内存的地址

②size是调整后的内存大小

③返回值为调整后的内存的起始地址

④realloc函数在调整原内存大小的基础上,还会将原来内存上存储的数据移动到新开辟的内存上

⑤realloc函数在调整内存大小的时候存在两种情况

        ●情况1:原有空间后面还有足够的未使用的空间可以用来扩大内存

          例1:

#include<stdio.h>
#include<stdlib.h>
int main()
{
	int* ptr = NULL;
	ptr = (int*)malloc(40);//先开辟40个字节的空间
	if (NULL == ptr)
	{
		perror("malloc");
		return 1;
	}
	printf("%p\n", ptr);打印动态内存空间的起始地址
	int* p = NULL;
	p = realloc(ptr, 60);//调整内存需要先用新的指针接收,不能用ptr接收,因为如果用ptr接收,调整内存失败,返回NULL指针,ptr无法指向原来开辟的空间,造成内存泄漏
	if (NULL == p)
	{
		perror("realloc");
		return 1;
	}
	ptr = p;
	printf("%p\n", ptr);

	return 0;
}

原空间地址和新开辟空间的地址相同

        ●情况2:原有空间后面的没有足够大的空间用来扩大内存

         realloc函数执行的操作包括:

        1. 开辟新的适合大小的空间

        2. 将旧的空间的数据拷贝到新空间

        3. 释放旧的空间

        4. 返回新空间的起始地址

        例1:

#include<stdio.h>
#include<stdlib.h>
int main()
{
	int* ptr = NULL;
	ptr = (int*)malloc(40);
	if (NULL == ptr)
	{
		perror("malloc");
		return 1;
	}
	printf("%p\n", ptr);
	int* p = NULL;
	p = realloc(ptr, 800);//调整后的内存太大,原空间后面没有足够的地址,因此,realloc重新开辟了空间,并返回一个新的指向该空间的地址
	if (NULL == p)
	{
		perror("realloc");
		return 1;
	}
	ptr = p;
	printf("%p\n", ptr);
	free(ptr);
	ptr = NULL;
	return 0;
}

原空间地址和新空间地址不同

        例2:

#include<stdio.h>
#include<stdlib.h>
int main()
{
	int* ptr = NULL;
	ptr = (int*)malloc(40);
	if (NULL == ptr)
	{
		perror("malloc");
		return 1;
	}
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		*(ptr + i) = i;
	}
	int* p = NULL;
	p = realloc(ptr, 80);
	if (NULL == p)
	{
		perror("realloc");
		return 1;
	}
	ptr = p;
	for (i = 0; i < 20; i++)
	{
		printf("%d\n", *(ptr + i));
	}
	free(ptr);
	ptr = NULL;
	return 0;
}

新开辟出来的多的空间并不会被初始化

例3:减小动态内存

#include<stdio.h>
#include<stdlib.h>
int main()
{
	int* ptr = NULL;
	ptr = (int*)malloc(40);//申请10个整型大小的空间
	if (NULL == ptr)
	{
		perror("malloc");
		return 1;
	}
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		*(ptr + i) = i;
	}
	int* p = NULL;
	p = realloc(ptr, 20);//调整为5个整型大小的空间
	if (NULL == p)
	{
		perror("realloc");
		return 1;
	}
	ptr = p;
	for (i = 0; i < 10; i++)//还是访问了10个整型,越界访问
	{
		printf("%d\n", *(ptr + i));
	}
	free(ptr);
	ptr = NULL;
	return 0;
}

缩小内存后,原内存后面不要的空间会被释放并被其他程序使用

 5. 常见的动态内存错误

5.1 对NULL指针的解引用操作

#include<stdio.h>
#include<stdlib.h>
int main()
{
	int* ptr = NULL;
	ptr = (int*)malloc(40);
	*ptr = 20;//错误,如果开辟内存失败,则对NULL进行解引用操作,应该对ptr进行判断
	free(ptr);
	ptr = NULL;
	return 0;
}

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

#include<stdio.h>
#include<stdlib.h>
int main()
{
	int* ptr = NULL;
	ptr = (int*)malloc(40);
	if (NULL == ptr)
	{
		perror("malloc");
		return 1;
	}
	int i = 0;
	for (i = 0; i <= 10; i++)
	{
		*(ptr + i) = i;//i = 10时造成越界访问,程序出错
	}
	free(ptr);
	ptr = NULL;
	return 0;
}

5.3 对非动态开辟内存的释放

#include<stdio.h>
#include<stdlib.h>
int main()
{
	int a = 10;
	int* ptr = &a;
	free(ptr);
	ptr = NULL;
	return 0;
}

5.4 使用free函数释放一块动态开辟内存的一部分

#include<stdio.h>
#include<stdlib.h>
int main()
{
	int* ptr = NULL;
	ptr = (int*)malloc(40);
	if (NULL == ptr)
	{
		perror("malloc");
		return 1;
	}
	int i = 0;
	for (i = 0; i <= 10; i++)
	{
		*ptr++ = i;//++操作使得ptr不再指向动态开辟内存的起始位置
	}
	free(ptr);//释放时会出错
	ptr = NULL;
	return 0;
}

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

#include<stdio.h>
#include<stdlib.h>
int main()
{
	int* ptr = NULL;
	ptr = (int*)malloc(40);
	free(ptr);
	free(ptr);//重复释放,出错
	ptr = NULL;
	return 0;
}

5.6 动态开辟内存忘记释放(内存泄漏)

动态内存申请的空间不会出了作用域就自动销毁,只能①free函数释放②程序退出自动归还给系统

#include<stdio.h>
#include<stdlib.h>
void test()
{
	int* p = (int*)malloc(40);
	if (NULL == p)
	{
		perror("maollc");
		return;
	}
	*p = 20;//使用后忘记释放内存
}
int main()
{
	test();//出了test函数,局部指针变量p销毁,无法找到动态开辟内存的位置,造成内存泄漏
             malloc申请的40个字节再也无法使用,直到程序退出
	return 0;
}

6. 经典笔试题解析

6.1 传值和传址

void GetMemory(char *p)
{
 p = (char *)malloc(100);//函数形参是实参的临时拷贝,p保存的动态内存的地址并不会返回给str
}
void Test(void)
{
 char *str = NULL;
 GetMemory(str);//传值,并不会改变实参
 strcpy(str, "hello world");//对空指针的解引用
 printf(str);//没有释放动态内存
}

正确写法:

#include<stdlib.h>
#include<stdio.h>
#include<string.h>
void GetMemory(char** p)
{
	*p = (char*)malloc(100);
}
void Test(void)
{
	char* str = NULL;
	GetMemory(&str);//传递str的地址,str就存储了动态内存的起始地址
	strcpy(str, "hello world");
	printf(str);
	free(str);//释放内存
	str = NULL;
}
int main()
{
	Test();
	return 0;
}

6.2 返回栈空间地址的问题

char *GetMemory(void)
{
 char p[] = "hello world";
 return p;//出了GetMemory函数,保存helloworld字符串的数组被销毁,还给操作系统,
}
void Test(void)
{
 char *str = NULL;
 str = GetMemory();//str指向的空间已经还给操作系统,str变成野指针
 printf(str);//对野指针的解引用,非法访问
}

6.3 忘记释放内存

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 GetMemory(char **p, int num)
{
 *p = (char *)malloc(num);
}
void Test(void)
{
 char *str = NULL;
 GetMemory(&str, 100);
 strcpy(str, "hello");
 printf(str);
 free(str);
 str = NULL;
}

6.4 释放空间后要将指针置为空

void Test(void)
{
 char *str = (char *) malloc(100);
 strcpy(str, "hello");
 free(str);//释放内存后要及时将指针置为空
 if(str != NULL)//指针没有置为空,判断为真,执行语句
 {
 strcpy(str, "world");
 printf(str);
 }
}

正确写法:

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

7. 柔性数组

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

7.1 柔性数组的定义

#include<stdio.h>
typedef struct st_type
{
	int i;
	int a[0];//柔性数组成员
}type_a;
typedef struct st_type1
{
	int i;
	int a[];//柔性数组成员
}type_b;
int main()
{
	printf("%d\n", sizeof(type_a));//4,结构体的大小不包括柔性数组的大小
	printf("%d\n", sizeof(type_b));//4
	return 0;
}

7.2 柔性数组的特点

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

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

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

7.3 柔性数组的使用

typedef struct st_type1
{
	int i;
	int a[];
}type_a;
int main()
{
	int i = 0;
	type_a* p = (type_a*)malloc(sizeof(type_a) + 100 * sizeof(int));
                        //柔性数组a获得了100个整型大小的空间
	p->i = 100;
	for (i = 0; i < 100; i++)
	{
		p->a[i] = i;
	}
	free(p);
	p = NULL;
	return 0;
}

完结,撒花!🌸🌸🌸

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值