一、动态内存存在的意义
在C语言中,我们创建数组的时候,要么对齐进行初始化,要么表明数组的大小,且数组的大小表明的时候,只能用常量(虽然C99标准中允许了这种行为,但是没有流行起来,这样写在VS上面是错误的。)如int arr[] = {1,2,3,4};int arr[10];等,且数组的大小一旦确定是改不了的。数组的大小在一些情况则无法满足程序员的需求(需要的时候,数组太小,不需要的时候,数组太大)。
为了解决变长数组这个问题,C语言中引入的动态内存的开辟,让程序员去自由的申请,与释放内存。
二、动态内存的介绍
内存可以主要分为,栈区,堆区,静态区,而我们动态内存开辟的空间则主要存储在堆区。堆区的内存空间一般是由程序员自己申请与释放。
二、动态内存中的四种内存分配函数
1.malloc函数
其作用是申请内存块,其函数声明是:void *malloc( size_t size );,其参数表示的是要开辟的字节大小。若开辟成功,返回值为指向改连续内存的地址,若开辟失败,则返回NULL,空指针。
注size=0,malloc的行为未定义,这是取决于编译器的。 下面看例子
动态内存的使用,离不开这三点,申请-使用(包含其内存大小的调整)-释放,首先malloc的返回值为void*,是无参,故需要强制类型转换,其次在使用的时候,为了培养我们良好的代码风格,故需要用一个if语句,来判断是否申请空间失败,而返回的空指针,一般我们会用上strerror函数,与errno的宏定义,这样会打印出我们错在哪里。最后用free函数释放,且要注意将指针变量赋值为NULL,
2.calloc函数
其作用与malloc函数一样,区别是,calloc函数会将开辟的内存空间初始化为0,而malloc函数负责开辟。其函数声明是:void *calloc( size_t num, size_t size );,第一个参数表示要开辟多少个元素,第二参数表示要开辟的每个元素的大小。返回值如上。
calloc除了比malloc多了初始化为0这一步,其余一样,没什么可补充的。
3.realloc函数
调整动态开辟的内存大小,其函数声明是:void *realloc( void *memblock, size_t size );,第一个参数表示,要修改的原内存空间的地址,第二参数表示,要修改之后的内存大小。原内存空间后面内存足够,则直接在原空间后面追加空间,若不足,则会在堆区重新开辟一片空间,将原空间的数据拷贝到新空间中,并对之前的内存空间进行销毁,此时返回新的空间的地址。若申请内存空间成功,则返回那片内存空间的地址(或为另开辟的或为旧地址),申请失败,返回NULL。
我们已知道realloc存在申请空间失败,返回空指针的可能性,那我问你,你们希不希望,申请空间失败了,还返回来空指针,将指向先前内存块的指针变量内容改变了?这样我们就无法再找到之前的内存空间了。所以我们需要创建一个临时变量,来排除其是空指针的情况,然后再将地址赋给,原指针变量。
4.free函数
专门用来释放动态开辟的空间块,其函数声明是:void free( void *memblock );,其参数表示要释放内存块的地址。
注意free函数释放内存块之后,其指针仍指向那个内存块的地址。所以我们一般会将其变量赋予NULL。
free函数释放非动态开辟的空间地址,其free的行为是未被定义的。
若参数memblock指向的是NULL,则函数free则什么都不做。
三、动态内存中常见的错误
1.对NULL指针进行解引用操作
对空指针解引用,会造成程序崩溃,且很有可能会产生动态内存错误,所以我们应时刻注意排查申请空间失败返回空指针的情况。
2.对动态开辟的空间越界访问
这个意思很明显,比如你就开辟了5个字节的空间,你非得访问第5个字节之后的内存空间,则会非法访问。
3.对非动态开辟的空间使用free函数释放
在讲述free函数的时候,就已告知,free函数是专门用来释放动态开辟的内存空间,你若对非动态开辟的空间进行释放,其行为是未可知的。
4.对动态开辟的部分空间使用free函数释放
5.对同一块内存空间进行多次释放
这里就是多次释放的情况,因为free释放动态内存的时候,不会讲指针变量赋值为NULL,所以为了避免这一错误,每次释放动态内存后,应养成将指针变量赋值为NULL的情况。因为free对NULL参数是不发生作用的
6.内存泄漏
忘记释放不在使用的内存空间会造成内存泄漏,其危害不容小觑。