网站建设所用软件,免备案网站怎么备案域名,wordpress 考试系统,工商网企业信息查询系统官网前言#xff1a;
随着我们深入的学习c语言#xff0c;之前使用的静态内存分配已经难以满足我们的实际需求。比如前面我们的通讯录功能的实现#xff0c;如果只是静态内存分配#xff0c;那么也就意味着程序开始的内存分配大小就是固定的#xff0c;应该开多大的空间呢
随着我们深入的学习c语言之前使用的静态内存分配已经难以满足我们的实际需求。比如前面我们的通讯录功能的实现如果只是静态内存分配那么也就意味着程序开始的内存分配大小就是固定的应该开多大的空间呢开大了是浪费开小了又不能满足自己的需求。而动态内存分配可以完美的解决这个问题真正地做到需要多少空间就开多大的空间根据需要动态地分配和释放内存空间.
总的来说动态内存分配比静态内存分配更灵活效率也更高避免了空间的浪费。
下面就开始动态内存的学习吧。
1.动态内存函数 1.malloc 2.free 3.calloc 4.realloc 这些函数都声明在stdlib头文件中。 1.1 malloc和free
malloc:
void* malloc (size_t size);
malloc函数向内存申请一块连续可用的空间并返回指向这块空间的指针。 参数size表示需要开辟的空间字节大小返回类型为void*,泛型指针指向被开辟空间首元素地址。具体指向什么类型的数据需要使用者自己决定。 需要注意的是如果开辟空间失败这个函数会返回NULL所以为了避免使用空指针在使用这个函数之后需要判断是否返回了NULL.
free:
void free (void* ptr);
free函数用来释放掉动态开辟的内存。 参数prt表示的是需要释放动态内存空间的指针。 这个函数非常重要因为如果动态内存分配的空间没有及时释放的话可能会造成内存泄漏而长时间运行的程序中存在内存泄漏会逐渐消耗系统的可用内存最终可能导致程序崩溃或系统变得不稳定。
所以为了避免内存泄漏的情况发生我们应该养成即使释放动态内存的习惯。
举例
int main() {//使用malloc动态开辟空间的例子int num 0;scanf(%d, num);int* ptr NULL;ptr malloc(num * sizeof(int));//开辟了num个整数类型元素大小的空间返回这片空间的起始位置给ptrif (ptr NULL) { //判断是否申请空间成功perror(malloc);}//初始化申请空间的元素for (int i 0; i num; i) {*(ptr i) i;}//释放空间防止空间泄漏指针置空防止野指针free(ptr);ptr NULL;return 0;
}
这里我输入的num10.
监视 我们发现ptr指针确实指向了10个整型大小的空间能正常初始化。
释放空间后 为什么要释放掉空间后将ptr置空
这是因为虽然我们将动态开辟的空间释放掉了但是ptr依旧指向这片空间但是此时ptr已经没有权限操作这片空间了所以我们需要置空。
1.2 calloc
void* calloc (size_t num, size_t size);calloc函数与malloc函数相似都是向内存申请一块连续可用的空间并返回指向这块空间的指针区别是calloc申请的空间里的每个字节会默认初始化为0。 第一个参数num是元素的个数size表示的是元素的字节大小开辟num个大小为size的元素的空间返回开辟空间的起始地址。 其实malloc和calloc的用法也基本一致只不过比malloc更加省事不用担心初始化的问题。
举例
int main() {int* p (int*)calloc(10, sizeof(int));if (p NULL) {perror(calloc);//打印错误信息}free(p);p NULL;return 0;
}监视指针p: 我们可以看到在申请到空间后空间里的元素都被初始化为了0.
1.3 realloc
void* realloc (void* ptr, size_t size);
realloc函数可以做到对动态开辟内存大小的调整如果此时申请的动态空间不够那么我们可以使用realloc函数来进行“扩容”。 ptr 是要调整的内存地址size 调整之后新大小返回值为调整之后的内存起始位置。 这个函数调整原内存空间大小的基础上还会将原来内存中的数据移动到新的空间。 realloc申请空间会遇到两种情况
1.原空间后面有足够空间,会直接在原来ptr指向空间后面追加返回的地址也是ptr指向的地址。 2.原有空间后面空间不够那就在堆空间另找一片空间这个时候返回的的地址就会是一个随机值。 举例
int main()
{int* ptr (int*)malloc(120);int ptr2 ptr;//记录原来ptr指向的地址if (ptr NULL){perror(malloc);}//扩展容量ptr (int*)realloc(ptr, 1000);if (ptr NULL)//如果申请失败返回NULL{perror(realloc);}if (ptr ptr2) {printf(情况1\n);}else {printf(情况2\n);}free(ptr);ptr NULL;return 0;
} 我们看到realloc申请的空间返回的地址不是原来的ptr指向的地址.
我们再来改一下代码
int main()
{int* ptr (int*)malloc(120);int ptr2 ptr;if (ptr NULL){perror(malloc);}//扩展容量ptr (int*)realloc(ptr, 140);//扩容的空间很小if (ptr NULL)//如果申请失败返回NULL{perror(realloc);}if (ptr ptr2) {printf(情况1\n);}else {printf(情况2\n);}free(ptr);ptr NULL;return 0;
}现在我们把realloc申请的空间改小一点为了能找到符合情况1realloc在原来ptr指向空间后面追加. 当然这也只是偶然情况就算是扩容改小一点也不一定就会直接追加空间在ptr指向空间后面。 2.常见的动态内存错误
在介绍了几个动态内存函数的用法之后我们再来看看在使用动态内存函数时经常犯的错误吧。
2.1 对NULL指针的解引用操作
void test()
{int *p (int *)malloc(INT_MAX/4);*p 20;//如果p的值是NULL就会有问题free(p);
}如果我们申请的空间太大就有可能开辟空间失败这个时候指针p接收到的就是NULL,而对NULL解引用操作就会导致错误。 2.2 对动态开辟空间的越界访问
void test()
{int i 0;int *p (int *)malloc(10*sizeof(int));if(NULL p){exit(EXIT_FAILURE);}for(i0; i10; i){*(pi) i;//当i是10的时候越界访问}free(p);
}我们这里只是申请了10个整型元素大小的空间这个时候*p10其实已经越界访问了此时程序就会出错。
2.3 对非动态开辟内存使用free释放
void test()
{int a 10;int *p a;free(p);//ok?
}上面我们已经知道了free是用来释放动态内存的空间的而如果对非动态开辟内存使用free释放等待你的就是这个 2.4 使用free释放一块动态开辟内存的一部分
void test()
{int *p (int *)malloc(100);p;free(p);//p不再指向动态内存的起始位置
}在执行malloc函数分配内存后指针p指向动态内存的起始位置。然而在将指针p增加1后它不再指向分配的内存起始位置。因此当在之后使用free函数来释放内存时由于free函数要求传入指向malloc分配内存起始位置的指针传入p将导致未定义的行为。这可能会导致程序崩溃或出现其他问题。
2.5 对同一块动态内存多次释放
void test()
{int *p (int *)malloc(100);free(p);free(p);//重复释放
}重复释放同一内存块会导致未定义的行为可能会导致程序崩溃或出现其他问题。
2.6 动态开辟内存忘记释放内存泄漏
void test()
{int *p (int *)malloc(100);if(NULL ! p){*p 20;}
}
int main()
{test();while(1);
}内存泄漏指的是程序在执行期间申请了一定量的内存空间但在使用完毕后没有及时释放导致这部分内存空间永远无法被程序使用从而浪费了宝贵的系统资源。所以我们一定要及时释放掉申请的空间这样才能“有借有还再借不难”。
总结
学习动态内存是如何分配的可以让我们以后写出来的程序更加高效灵活这也是我们作为程序员的基本素养同时我们也应该注意正确使用动态内存分配避免一些常见的错误。