持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第24天,点击查看活动详情
前言
本文是针对以前讲解动态内存管理的文章的一些补充内容,以及简单提一下命令行参数是什么。
笔者水平有限,难免存在纰漏,欢迎指正交流。
动态内存补充内容
为什么要有动态内存
在技术方面,普通的空间申请,都是在全局或者栈区,全局一般不太建议大量使用,而栈空间较为有限,那么如果一个应用需要大量的内存空间的时候,需要通过申请堆空间来支持基本业务。
在应用方面,程序员很难一次预估好自己总共需要花费多大的空间。想想之前我们定义的所有数组,因为其语法约束,我们必须得明确"指出"空间大小。但是如果用动态内存申请(malloc),因为malloc是函数,而函数就可以传参,也就意味着,我们可以通过具体的情况,对需要的内存大小进行动态计算,进而在传参申请,提供了很大的灵活性
指针合法性
指针如果有具体的指向(包括野指针)的话,对应的合法性我们是无法验证的,因为确认指针具体值的“合法性”并不是我们用户能做到的。
但是在遵循“所有指针如果没有被直接使用必须设置为NULL”这一规范时,我们在函数内部想要验证指针的“合法性”,本质上就是在验证指针值不为NULL。
这里的“合法性”指的是能够被用户直接使用。
开辟内存比你申请的要多
我们知道内存泄漏是在堆区申请了空间使用完后不释放。那如果程序退出了,内存泄漏问题还存在吗?不在了,因为此时系统会强制回收内存。
那free()只传入了申请的堆空间的起始地址,并没有指定要释放多少空间呀,它到底是如何正确释放申请的空间的呢?
我们来看个例子:
当free后,发现释放的空间实际上要比10个字节多得多,由此可知,申请的空间一定不止10个字节。
malloc申请空间时,系统分配的其实比你要求的更多,而多出来的部分用来记录这次申请的更详细的信息,比如申请时间、申请的大小等等。
申请堆空间的时候,一般建议申请大空间,要申请小空间的话可以在栈上申请。
如何理解free()释放
我们发现在释放之后p的内容并没有发生改变,但是此时已经无法再通过p来访问堆空间了,那所谓的释放究竟是在干什么呢?
举个生活中的例子:
假设你找了个女朋友,名叫如花,这个时候你和她之间至少是建立了这么一段恋爱关系。然而在一天天的相处后,她愈发无法容忍你的懒惰:每天没写几行代码还一直打游戏,于是乎,她给你发了好人卡,此后和平分手,也就是说取消了你们的关系。虽然已经分手了,但你无法忘却她的名字,更忘不了有她的朝朝暮暮,你终于忍不了了,打算去找她,但是被拒绝了,因为你们之间已经没有半毛钱关系了。
对应起来的话,就是说指针p在动态内存申请的时候和申请的堆空间建立了关系,可以通过p的内容找到并使用堆空间,而在free释放掉以后,p的内容没有改变(你忘不了她的名字),但是取消了p与堆空间的关系(已经分手了),也就无法再用p访问那块空间了。
总而言之:free是在取消指针与申请的堆空间的关系,那释放的是啥,是申请的堆空间的内容,前面也提到过,里面会包含额外信息,指针与堆空间的关系需要被维护,靠的就是一段数据来维护,所谓的额外信息也就包含这一数据,释放掉堆空间内容后额外信息里的关系数据就被释放,这样一来指针和申请的堆空间就“一刀两断”了。
不过free后指针的内容还在,仍有可能通过间接的手段被使用,这是不应当发生的,有可能存在安全隐患,只是编译器不会将指针置为NULL,得用户自己注意,所以一般还会在free后手动加上诸如p = NULL之类的指针值置零。
总结
内存管理的本质其实是:空间什么时候申请,申请多少,什么时候释放,释放多少的问题。
场景:C的内存管理工作是由程序员决定的,而程序员什么时候申请,申请多少,什么时候释放,释放多少都是由场景决定的(比如上面的链表操作),而大部分书中,是讲具体操作,很少有场景,所以管理工作体现的并不直观。不过我们现在能理解即可。
其他高级语言:像java这样的高级语言,语言本身自带了内存管理,所以程序员只管使用即可。换句话说,内存管理工作,程序员是不用关心的。但是C是较为底层的语言,它的内存管理工作是暴露给程序员的,从而给程序员提供了更多的灵活性,不过,管理工作也同时交给了程序员。在C中,程序员+场景=内存管理。
命令行参数(简单了解)
main函数也是一个函数,其实也可以携带参数的
int main( int argc, char *argv[], char *envp[] )
{
//program-statements
}
那这里是可以有三个参数的。
第一个参数: argc 是个整型变量,表示命令行参数的个数(含第一个参数)。
第二个参数: argv 是个字符指针的数组,每个元素是一个字符指针,指向一个字符串。这些字符串就是命令行中的每一个参数(字符串)。
第三个参数: envp 是字符指针的数组,数组的每一个原元素是一个指向一个环境变量(字符串)的字符指针。
本质:命令行参数获取在命令行上传入的一个个的选项,可以让程序根据命令行参数具有不同的功能。
在VS下设置命令行参数:
右击项目,打开属性,在调试中输入对应命令参数
#include <stdio.h>
int main(int argc, char* argv[], char* envp[])
{
int i = 0;
for(i=0; i<argc; i++)
{
printf("%s\n", argv[i]);
}
return 0;
}
注意:argv数组的最后一个元素存放了一个 NULL 的指针。
关于char* envp[]
本质其实是获取系统相关环境变量内容,这个简单了解一下即可。
int main(int argc, char* argv[], char* envp[])
{
int i = 0;
while(envp[i] != NULL)
{
printf("%s\n", envp[i]);
i++;
}
return 0;
}
运行起来结果如下:
envp 数组的最后一个元素也存放 NULL 指针。
要更加深入学习的话需要学习linux和操作系统相关知识,仅凭C语言是绝对不够的,本文只限制于C语言,就不再讲解了。
以上就是本文全部内容,感谢观看,你的支持就是对我最大的鼓励~