我们之前开辟的空间,大小固定,且在申明数组的时候,必须指定数组的长度。但是有时候我们需要的空间大小在程序运行的时候才知道,这就得动态内存开辟出马了。
目录
1.malloc和free
2.calloc
3.realloc
4.常见动态内存错误
5.经典笔试题
//两种动态内存传参
//非法访问(返回栈空间问题)
6.C/C++程序的内存开辟
7.柔性数组
1.malloc和free
头文件:#include<stdlib.h>
格式:void* malloc ( size_t size )
size为申请的字节大小;
功能:向内存申请一块连续可用的空间,并返回指向这块空间的指针。
没有初始化,不赋初值时默认为随机值;
如果开辟成功,则返回一个指向开辟好空间的指针;
如果开辟失败,则返回一个NULL指针,因此malloc的返回值一定要做检查;开辟空间过大时,也会开辟失败,Not enough space。
申请到的空间没有初始化,直接返回起始地址。
头文件:#include<stdlib.h>
格式:void free (void* ptr);
功能:释放动态开辟的空间。
如果参数ptr指向的空间不是动态开辟的,那么free的行为是未定义的;
如果参数ptr是NULL指针,那么free函数什么都不做。
#include<errno.h>
#include<string.h>
#include<stdlib.h>
int main()
{
	//申请40个字节,用来存放10个整型
	int* p = (int*)malloc(40);
	//开辟失败,用strerror报错
	if (p == NULL)
	{
		printf("%s\n", strerror(errno));
		return 1;
	}
	//开辟成功,存放1~10
	int i = 0;
	for (i = 0; i < 10; i++)
	{
        //赋值
		*(p + i) = i + 1;
		//打印
		printf("%d ", *(p + i));
	}
	//用free来释放申请的空间
	free(p);
	p = NULL;//手动将p置为空指针
	return 0;
} 
2.calloc
头文件:#include<stdlib.h>
格式:void* calloc (size_t num, size_t size);
num是开辟的元素个数,size是每个元素的大小;
功能:申请空间后会把空间初始化为0,然后返回起始地址。
开辟内存成功或失败同malloc。
#include<errno.h>
#include<string.h>
#include<stdlib.h>
int main()
{
	int* p = (int*)calloc(10, sizeof(int));
	//开辟失败
	if (p == NULL )
	{
		printf("%s\n", strerror(errno));
		return 1;
	}
	//开辟成功
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", *(p + i));
	}
	free(p);
	p = NULL;
	return 0;
}
 
3.realloc
头文件:#include<stdlib.h>
格式:void* realloc (void* ptr, size_t size);
功能:对动态开辟的内存进行调整。
情况1:后面有足够的空间可以扩容,直接在后面续上新的空间,返回旧空间的起始地址;
情况2:后面没有足够的空间可以扩容,找一个满足空间大小的新的连续空间,把旧的空间里的数据拷贝到新的空间,并且把旧的空间释放,返回新空间的地址;
情况3:开辟失败,返回NULL。
若传的是空指针NULL,则相当于malloc。
#include<errno.h>
#include<string.h>
#include<stdlib.h>
int main()
{
	int* p = (int*)malloc(5 * sizeof(int));
	if (NULL == p)
	{
		perror("malloc");
		return 1;
	}
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		*(p + i) = 1;
	}
	//不够,要增加5个整型的空间
	int* ptr = (int*)realloc(p, 5 * sizeof(int));
	if (ptr != NULL)//开辟成功
	{
		p = ptr;
        ptr = NULL;//更安全
	}
	//继续使用空间
	for (i = 0; i < 10; i++)
	{
		printf("%d ", *(p + i));
	}
	//释放空间
	free(p);
	p = NULL;
	return 0;
} 
4.常见动态内存错误
1> 对NULL指针的解引用操作:返回值一定要检查;
2> 对动态开辟内存的越界访问:没开辟的内存不能访问;
3> 对非动态开辟内存使用free释放:free只能释放动态开辟内存;
4> 使用free释放一块动态开辟内存的一部分:释放的p指针必须指向起始地址;
5> 对同一块内存多次释放:避免重复释放或在每次释放后牢记将p指针置为NULL;
6> 动态开辟内存忘记释放(内存泄漏):free与内存函数一定要成对使用。
5.经典笔试题
这几个题目原题(出处:《高质量的C/C++编程》)是给出错误的代码让我们改错,这里有的就直接放上正确的代码了哈~
//两种动态内存传参
char* GetMemory1()//传值,返回地址
{
	char* p = (char*)malloc(100);
	return p;
}
void test1()
{
	char* str = NULL;
	str = GetMemory1();
	strcpy(str, "hello world!");
	printf(str);//哦吼吼还可以这样用
	free(str);//一定不能忘!
	str = NULL;
}
void GetMemory2(char** p)//传地址
{
	*p = (char*)malloc(100);
}
void test2()
{
	char* str = NULL;
	GetMemory2(&str);
	strcpy(str,"hello world!");
	printf(str);
	free(str);//一定不能忘!
	str = NULL;
}
int main()
{
	test1();
	test2();
	return 0;
} 
//非法访问(返回栈空间问题)
char* GetMemory()
{
	char p[] = "hello world!";
	return p;
}
void test()
{
	char* str = NULL;
	str = GetMemory();
	printf(str);
}
int main()
{
	test();
	return 0;
} 
上述代码存在问题,打印结果是一堆乱码(随机值)。
解释:进入GetMemory函数后,内存为p数组开辟了一段空间存放hello world, 并返回首元素地址p,但是在出GetMemory函数后该内存就被还给了操作系统,该数组就被销毁了,此时再利用str访问p指向的空间就会形成非法访问,打印乱码。
将 char p[] 改为 char* p 或在前面加上 static 即可成功打印。
6.C/C++程序的内存开辟

7.柔性数组
定义:在C99标准下,结构体中的最后一个元素允许是未知大小的数组,这就是『柔性数组』成员。
特点:1> 结构中的柔性数组成员前面至少有一个其他成员;
2> sizeof 返回的这种结构体大小不包括柔性数组的内存;
3> 包含柔性数组成员的结构用malloc ()函数进行内存的动态分配,并且分配的内存应该大于结构的大小,以适应柔性数组的预期大小。
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<errno.h>
struct S
{
	int n;
	char arr[];//或arr[0]
	          //表示数组大小未知,不是大小无限大
};
int main()
{
	printf("%d\n", sizeof(struct S));//4
	//创建空间
	struct S* ps = (struct S*)malloc(sizeof(struct S) + 10 * sizeof(char));//多开辟空间适应柔性数组大小
	ps->n = 100;
	int i = 0;
	//赋值
	for (i = 0; i < 10; i++)
	{
		ps->arr[i] = 'c';
	}
	//打印
	for (i = 0; i < 10; i++)
	{
		printf("%c", ps->arr[i]);//cccccccccc
	}
	//增容
	struct S* ptr = (struct S*)realloc(ps, sizeof(struct S) + 20 * sizeof(char));
	if (ptr != NULL)
	{
		ps = ptr;
	}
	else
	{
		perror("realloc");
		return 1;
	}
	//使用
	printf("%d\n", sizeof(struct S));//还是4
	//释放
	free(ps);
	ps = NULL;
	return 0;
} 
结束!!明天考C啦,Best wishes to me !


















