每天进步一点点,坚持带来大改变!!!
前言:
在C语言的学习过程中对于变量在内存中的存储首先需要向内存申请空间,有时候我们无法确定一次性申请多大的空间,这就要求我们对内存的使用更加灵活,当开始申请的内存不够的时候可以在原来的基础上继续申请空间
1.动态内存函数的介绍:
1.malloc
#include<stdlib.h>
void* malloc (size_t size);
这个函数向内存申请一块连续可用的空间,并返回指向这块空间的指针。
函数的使用规则:
如果开辟成功,则返回一个指向开辟好空间的指针;
如果开辟失败,则返回一个NULL指针;
返回值是void*,因此需要使用根据实际情况进行类型强转;
如果size的值为0,则标准是未定义的,取决于编译器。
2.free
void free (void* ptr);
用来做动态内存的释放和回收的
函数的使用规则:
如果参数ptr指向的空间不是动态开辟的,那么free函数的行为是未定义的
如果ptr是NULL指针,则函数什么事情都不做
3.calloc
void* calloc (size_t num, size_t size);
函数的使用规则:
为num个大小为size的元素开辟一块连续空间,并且把空间的每个字节初始化为0;
与malloc函数不相同的是会把申请空间的每个字节初始化为0,其它的都相同
4.realloc
void* realloc (void* ptr, size_t size);
可以灵活调整内存大小
函数使用规则:
ptr是要调整内存的起始位置;
szie是调整后的总大小;
返回值为调整之后内存的起始位置。
realloc调整内存有两种情况:
情况1:在原有的空间上继续追加空间,原来空间数据不发生变化
情况2:在原来的空间上继续追加空间时,后续没有足够的空间,所以会向内存重新找一块空间,并且会将原来的数据拷贝放到新申请的空间。
2.常见的动态内存错误:
1.对NULL指针进行解引用:
int* p = (int*)malloc(8000); *p = 20; free(p); p = NULL;
2.对动态开辟的内存进行越界访问:
int* p = (int*)malloc(40); if (p == NULL) { printf("%s\n", strerror(errno)); } int i = 0; for (i = 0; i <= 10; i++) { *(p + i) = i; } for (i = 0; i <= 10; i++) { printf("%d ", *(p + i)); } free(p); p = NULL;
3.对非动态开辟的内存进行free释放:
int a = 10; int* p = &a; free(p); p = NULL;
4.使用free释放动态开辟的一部分空间:
int* p = (int*)malloc(40); if (p == NULL) { printf("%s\n", strerror(errno)); } int i = 0; for (i = 0; i < 5; i++) { *p = i; p++; } free(p); p = NULL;
5.对同一块动态内存进行多次释放:
int* p = (int*)malloc(50); free(p); //…… free(p);
6.动态开辟的内存忘记释放(内存泄漏):
void test() { int* p = (int*)malloc(40); if (p != NULL) { *p = 10; } } int main() { test(); return 0; }
test函数在调用结束后,函数栈帧还给操作系统,但是p原来指向的动态开辟的内存并没有释放掉。
3.关于动态开辟内存的经典笔试题:
1.题目一:
test函数运行会有什么结果?
void GetMemory(char*p) { p= (char*)malloc(100); } void test (void) { char* str = NULL; GetMemory(str); strcpy(str, "hello world"); printf(str); } int main() { test(); return 0; }
修改:
2.题目二:
char* GetMemory(void) { char p[] = "hello world"; return p; } void test(void) { char* str = NULL; str = GetMemory(); printf(str); } int main() { test(); return 0; }
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); } int main() { test(); return 0; }
修改:
4.题目四:
void test(void) { char* str = (char*)malloc(100); strcpy(str, "hello"); free(str); if(str!=NULL) { strcpy(str, "world"); printf(str); } } int main() { test(); return 0; }
修改:
4.柔性数组:
1.概念:
结构体中的最后一个元素可以是未知大小的数组,这就叫柔性数组。
2.柔性数组的特点:
1.结构中柔性数组成员前面至少包含一个其它成员;
2.sizeof返回的这种结构大小不包括柔性数组的大小;
3.包含柔性数组的成员的结构用malloc函数进行内存的动态分配 ,并且分配的大小应该大于结构体的总大小,已适应柔性数组的大小;
3.柔性数组的优点:
上述结构体也可以设计成下面这段代码:
相互比较柔性数组的优点:
1.方便内存的释放:
柔性数组释放内存的时候只需要释放一次就可以了,大大降低了动态内存开辟忘记释放的问题2.有利于访问速度:
有益于提高访问速度,也有益于减少内存碎片
总结:
以上是关于C语言动态内存开辟的知识点详解,希望能够在学习的过程中互相帮助,互相促进,欢迎大家点赞,留言评论!!!