什么是动态内存呢?
动态内存就是用malloc,calloc,realloc和free这些动态内存函数来进行向堆区申请空间,来交给程序员进行管理使用,这就动态内存管理。
malloc使用
malloc是申请一块空间,需要自己手动进行初始化,并且手动free和置空
![]()
calloc
calloc是开辟空间num个sizeof(类型)并初始化为0。
![]()
![]()
realloc
realloc有两种情况:
1.当源空间为0,即传入NULL时,和malloc作用一样。
![]()
![]()
2.如果做扩容空间时,即传入的指针有效时,又两种分为两种情况
2.1.如果原空间后面空间足够,那么直接在原空间后面直接开辟,返回原空间的最低地址。(即返回值和传入值相同)
![]()
![]()
2.2.如果原空间后面空间不够开辟的话,那么函数就会在堆区上,找一块能符合用户所需要的空间大小的空间,然后把原空间数据拷贝到函数开辟的空间上,然后把原空间释放,返回重新开辟的空间的最低的地址。
这个情况一般很少出现,所以不在举例子啦。
C程序地址空间
首先我们要明白堆区在C程序地址空间的那个地方呢?或者说C程序地址空间是什么呢?
我们今天我们主要聊下堆区。
为什么要有动态内存开辟呢?
首先我们知道平常申请空间可以直接在栈区上申请,那么为什么要有动态内存开辟呢?
举个例子:
我们要存放一些数字,这些数字可能很多,可能很少,这时我们该如何在栈区申请空间呢?
本着要全部存下的理念,我们要开辟空间大一点,但是栈区空间有限,稍微大点即形成栈溢出啦,这也就是为什么要有堆区的原因。
![]()
访问内存出错
比如定义一个结构体,其中结构体成员是动态开辟的,如果没有初始化就使用,那么就会导致访问内存出错。
struct stu { char* p; int age; }s1; int main() { strcpy(s1.p, "胡杨树下"); s1.age = 18; return 0; }这里的 s1.p 虽然是变量,但是没有相对性的空间,所以就会导致访问内存出错。
上面说啦,应该给 s.p开辟空间,下面就看看怎么开辟吧,下面的开辟是没问题的。
struct stu { char* p; int age; }s1; void Show(char *name) { printf("%s\n", name); } int main() { struct stu s1 = { NULL, 0 }; s1.p = (char *)malloc(sizeof(char)* 30); strcpy(s1.p, "胡杨树下"); Show(s1.p); free(s1.p); s1.p = NULL; return 0; }
指针合法性问题
首先,什么 "合法" 的指针呢?
一般来说,就是传递的指针是能正常使用的。
如果一个指针它是有指向的(野指针也是有指向的,只不过不知道指向哪里),那么我们没办法判断 "合法" 性。所以我们要求指针如果没有直接被使用,那么就应该赋值为NULL,这样我们验证 ”合法“ 性的问题时,就变成了对指针判断是否为NULL。
对上面代码改进下就会变为以下的代码:
struct stu { char* p; int age; }s1; void Show(char *name) { if (name == NULL) { exit(-1); } printf("%s\n", name); } int main() { struct stu s1 = { NULL, 0 }; s1.p = (char *)malloc(sizeof(char)* 30); if (s1.p == NULL) { exit(-1); } strcpy(s1.p, "胡杨树下"); Show(s1.p); free(s1.p); s1.p = NULL; return 0; }这里经过判断指针是否为NULL,这样就避免空指针的情况。也可使用 assert(宏)进行断言,引用头文件 "assert.h",但是这种宏只能在调试版本使用,不能用于发布版本。
内存越界
相信大家都听过内存越界这个话题,那么指针越界一定会报错吗?
指针的越界有时候时不会进行报错的,比如:
![]()
或者malloc之后没有free也是有概率不报错的。
![]()
但是不free会发生更严重的情况,叫内存泄漏。
内存泄漏
内存泄漏简单来说就是,程序只申请内存,不释放不free。
那么如果没有程序,或者程序退出啦,那么还有内存泄漏问题吗?
如果程序退出啦,那么操作系统会强制收回申请的内存。
内存泄漏最更害怕的是一些永远不会主动退出的程序,那么就很恐怖的事,一个进程,只申请内存,不释放,那么就没有内存可用啦。
free多大空间呢?
我们只知道free的起始地址,那么释放多大呢?
![]()
我们看下,free的大小远远比释放的空间要大,所以就可以说,我malloc的大小要比我们要申请要大。
因为malloc要申请的空间要有多余的空间来管理申请的空间,所以要比我们正常申请的要大,所以如果我们malloc要越界的话不一定会报错。
free释放是在干嘛?
free之后我们一般要把指针置空,为什么呢?不置空还会指向原空间吗?
![]()
我们能看出空间的指向没变,也就是说指向没变,那么变的是什么呢?
变的是p和堆区申请的关系,所以free就是改变的关系,free本质就是改变关系,也就是说p还指向堆区申请的空间,所以我们应该把这种关系断掉,就把p置为NULL。
就像你谈女朋友时,都分手啦,就不该留下联系方式啦,所以就该把联系方式断啦。