Q:为什么需要动态内存分配?
A:使用这两种方式开辟内存的大小是固定的:
int val = 20; //在栈上开辟4个字节的空间
char arr[10] = {0}; //在栈上开辟10个字节的连续空间
另外,数组在申明的时候,必须指定数组的长度,它所需要的内存在编译时分配。
但是,当所需空间的大小在程序运行时才能知道,也就无法事先开辟一块空间了,这时候就需要动态内存开辟。
注:以下均需包含
void* malloc (size_t size);
使用:
#include
#include
int main()
{
int* ptr = (int*)malloc(40);
if (ptr == NULL) //务必判断是否开辟成功,否则退出并报错
{
perror("malloc");
//或printf("%s",strerror(errno));
//strerror头文件是 errno头文件是
//直接打印用perror,如果只需要获得错误码不需要打印就使用strerror(errno)
return 1;
}
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", *(ptr + i));
}
//必不可少的使用后free 和 NULL置空
free(ptr);
ptr = NULL;
return 0;
}
易错:1.忘记判断NULL 2.忘记free和指针置空
free只是释放(返还)空间,但不会将其置为空指针,为了防止它是野指针、防止内存泄漏,需要手动置为空指针
注:free不释放等程序结束也能自动回收内存空间
void* calloc (size_t num, size_t size);
使用:
#include
#include
int main()
{
int* ptr = calloc(10, 4);
if (ptr == NULL)
{
printf("%s", strerror(errno));
return 1;
}
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", *(ptr + i));
}
free(ptr);
ptr = NULL;
return 0;
}
1.相同之处:
1.无符号整形(size_t)是要开辟内存的大小,以字节为单位
2.返回值void*表示适用于所有类型的指针来接收,并且需要强制类型转换来适应所要接受的类型指针
3.如果开辟成功,则返回一个指向开辟好空间的指针。
如果开辟失败,则返回一个NULL指针,因此对malloc、calloc以及realloc的返回值一定要做检查
(例如设置一个指针来存放返回的地址,判断是否为NULL,是NULL返回错误信息
如果不为NULL,再使用要使用的指针来存放,这样可以避免本身要使用的指针值改变)。
4.如果参数 size 为0,标准是未定义的,取决于编译器。
2.不同点
1.malloc开辟的空间是未初始化的,calloc会默认初始化为0,单位为字节
2.calloc中num参数表示要开辟num个size大小的字节,
比如calloc(3,4); 表示开辟3个大小为4个字节的内存,可以使用在数组中
3.相比于malloc,calloc的初始化更精确可控一些,建议使用calloc
void free (void* ptr);
使用malloc等动态内存开辟相关库函数时一定配合free使用,先free()后把指针置空,避免内存泄漏和野指针。
void* realloc (void* ptr, size_t size);
1.ptr 是要调整的内存地址
2.size 调整之后新大小
3.返回值为调整之后的内存起始位置。
使用:
#include
#include
int main()
{
int* p = (int*)calloc(10, sizeof(int));
if (p == NULL)
{
perror("calloc");
return 1;
}
//realloc从新分配大小为20个字节
int* ptr = (int*)realloc(p, 20 * sizeof(int));
if (ptr != NULL)
{
p = ptr;
}
//free释放
free(p);
p = NULL;
return 0;
}
总结:realloc开辟空间,后面空间足够时还是用原首地址,扩容(直接追加);若空间不足,则会开辟一块新的空间,(一块连续的空间),把原来的内容拷贝到新的地址内,此时地址改变。如果找不到合适的空间来调整大小,则返回空指针。所以一般不用原来的指针变量来接收值,防止值被改变。
实际上,普通的局部变量是在栈区分配空间的,栈区的特点是在上面创建的变量出了作用域就销毁。
但被static修饰的变量则存放在数据段(静态区),数据段的特点是在上面创建的变量,直到程序结束才销毁,所以生命周期变长了。
1.定义:在C99 中,结构中的最后一个元素允许是未知大小的数组,这就叫做『柔性数组』成员。
typedef struct st_type
{
int i;
int a[0];//柔性数组成员 (若无法编译,可改为int a[];)
}type_a;
2.特点:
3.使用:
//代码1
int i = 0;
type_a *p = (type_a*)malloc(sizeof(type_a)+100*sizeof(int));
//业务处理
p->i = 100;
for(i=0; i<100; i++)
{
p->a[i] = i;
}
free(p);
注意:对有柔型数组的结构体动态开辟时,要用sizeof() + —(100 * sizeof(int))的形式来分配大小
4.优点
使用柔型数组的好处是方便内存释放,一次性分配内存,例如用指针动态开辟时可能忘记释放两次,而用柔型数组做一次free就可以把所有的内存释放掉。
其次,有利于提高访问速度,因为是内存的开辟是连续的,也有益于减少内存碎片(没有被使用的一些内存)。
※推荐阅读※
C语言结构体里的成员数组和指针