大家好,我是轩辕。我发现了一个现象:
很多人学C语言编程,对内存模型很混乱,搞不清楚C语言层面的堆、栈和操作系统层面的虚拟内存之间的关系。
很多人知道C语言有提到静态变量区、动态变量区、常量区,却把这些和进程的地址空间对应不起来。
很多人知道C语言里面有提到栈,但却不知道实际上一个进程里面可能有很多个栈。
诸如此类问题,都是因为新手在学习的时候,同时接收到了C语言本身和操作系统层面的双重信息,又没能把二者关系理顺,导致学的时候脑子里一团浆糊。
这不由得让我想起我当初学习C语言的时候,同样遇到过这个问题,今天就给大家好好聊聊这个话题,保证让正在学习C语言的同学豁然开朗。
之所以会有这种情况出现,来源于C语言作为一种编程语言标准,在不同平台落地时候形态各异。
它不像Java,你要学Java的内存模型,只看JVM就够了,JVM提供了统一的内存模型。
但C语言作为一门古老的语言,它诞生的太早了,早在70年代初就诞生了,那时候,什么Windows、Linux、多线程这些东西都还没有出现。
因为C语言是一门系统级编程语言,它可以运行在各种CPU、单片机上,可以运行在各种操作系统Unix、Linux、Windows等等,那不同的平台构建出来的C语言运行时环境肯定是各有差异的。
比如虚拟内存这个概念,是现代操作系统引入的一种内存管理技术。现在我们知道,不管是Linux还是Windows,程序运行起来以后是一个进程,利用虚拟内存的机制,每个进程都是独立而完整的内存地址空间,其中有部分是用户态,有部分是内核态···
但要注意,上面这一段的描述都是操作系统层面的东西。而在这之前,C语言就已经诞生了,那时候的C语言就是使用的直接物理地址寻址的。甚至在单片机上,可能都没有进程这一概念,所以连“进程地址空间”这一词都不复存在。
有很多程序员学的C语言都是来自谭浩强的那本《C程序设计》,这本书就是彻头彻尾的在讲C语言这门语言本身,里面涉及到的涉及到内存相关的术语,比如堆、栈、静态内存区、动态内存区,这些都是在纯C语言这个范畴里面探讨。
而我们实际工作开发过程中,基本上都要在具体的平台上去落地,比如有人去开发基于单片机的C语言,有人开发基于ARM的C语言,有人开发基于Windows的C语言,有人开发基于Linux的C语言,这个时候,同一个术语在不同的平台落地的时候就有了不一样的意义。
比如我们经常说的栈这个概念,C语言里面就是一个抽象的概念,用来存放函数局部变量以及调用传参保存返回地址的地方。那具体在Windows操作系统上,就要和Windows/Linux的线程栈机制联系起来,ESP寄存器指向栈顶,默认分配有几个内存页面,有动态增长的机制等等。
这就是一个从理论到实际,从抽象到具象的例子。
又比如说堆这个概念,在C语言层面,就是一个动态分配内存的区域。但具体落实到Windows操作系统,C语言里面的堆和进程层面的堆可能还不是同一个东西,一个进程里面可以有很多个堆。
所以如果你只看完谭浩强的《C程序设计》就去做C项目开发,会发现各种不适应,一大堆东西跟书上讲的不一样,对新手来说这时候就迷茫了。
说了这么多,看起来有些零散,再收敛总结一下:
C语言是一门古老的语言,它可以运行在很多的平台上,它里面有很多跟内存相关的概念是不限于平台,具有普适性的抽象概念。
但我们真正在做开发的时候,只关注这些抽象概念是不够的,必须得结合具体开发平台去理解这些抽象的概念,给这些概念接上地气,才能理解的更透彻。
新手在学习相关的知识的时候,一定要注意区分,哪些是编程语言层面的东西,哪些是平台(操作系统、硬件)层面的东西,千万不要混为一谈,把自己弄晕。
以上就是今天的分享,大家觉得有用的话,别忘了点赞留言哦,还有什么关于编程的疑问,也可以留言告诉我。
话说,你在学C语言的时候,有遇到类似的困惑吗?