对一个程序,通常的理解就是,源码编译成机器代码,然后通过机器解释运行。不过是怎样编译成机器代码,和怎样运行的,无疑是个值得探讨的问题。怎样编译成机器代码,过程就是源码的编译、链接,编译器做了这些事。而怎样运行,却不是哪个器件自己一己之力就可以做到的。机器代码要在机器上运行,就得要请求硬件资源。涉及最多的就是CPU和内存了。CPU进行逻辑控制和运算,内存用于运行过程中的数据的快速交互场所。

一个C程序从其自身代码的结构上来看,编译过后不过是一段代码。而这段代码,从磁盘系统加载到内存中被称为代码段或正文段(code/text segment)的地方。在内存中的正文段是共享的。所以,当我们运行同一程序的多个进程时,在内存中,只有一个程序代码的副本。当然,为了防止有人要做坏事,正文段的权限常常都是只读的。
一个只有谋略,而手下无兵的将领,在战场上是什么也做不了的。程序代码,当然也不可能纯粹的都是逻辑描述。还必须得有逻辑作用的对象和结果——数据。也即程序中的各种变量。不同变量,在程序中的地位看起来除了作用域与生存周期外,都大同小异。不过,它们的实现确实非常的不同的。

程序中的未初始化的全局变量,存储在一个被称为未初始化数据段的地方。也即是bss(block started by symbol)段。同时,内核会自动的将此段中的数据初始化为0或空指针。如,函数外的声明:

int sum[10];

使得该变量存放在bss段中。
既然有了未初始化的全局变量,当然也就有初始化了的全局变量。它们存放在一个称为初始化数据段(常简称为数据段)的地方。拥有全局的作用域整个程序的生存周期。(Right?) 如,任函数外的声明:

int tmp = 99;

使得该变量存放在数据段中。
当然,还有一个叫堆栈段的东西。自动变量及每次函数调用是所需要保存的信息都存放在这里。堆栈的特点是先进后出(FILO)及数据存放的周期短,即进栈出栈操作很频繁。自动变量通常都是作为临时变量存在的,存在周期短,非常时候放在栈中。(Right?) 而函数调用时的的现场信息,可以利用堆栈的先进先出特点很方便的进行保护和恢复。典型的,递归的实现就是用了堆栈进行,因为堆栈是一层层向下增长的,所以在子函数中是不会覆盖调用函数的参数的。特别得要注意的一点是,主函数main中的变量,也是自动变量。因为主函数也是函数啊!
最后,作为C和C++特有的动态内存分配。是在运行时,利用堆来动态的进行内存分配的。所以动态分配的内存都具有全局的作用域(从分配后开始)。而生存周期而是直到其被释放前。动态内存的分配,其实就是向内核请求一块内存资源,而释放呢,就是将资源返还给内核。所以动态分配的内存都必须在不再需要时释放。不然可能会造成内存泄露及动态分配失败等恼人的问题。
看到这里,我们可以发现。一个C程序,其代码是被加载到了内存的代码段中,而其代码段中的一个个的变量,并没有实际的存放数据,数据根据情况的不同存放在了bbs段,数据段,堆栈段及堆中,代码段中的变量,存放的是一个指向各个实际存放地方的指针。(Right?)
典型的C内存分布图:

再说说C中动态内存管理。在C中,主要由标准头文件<stdlib.h>中定义的几个函数来进行内存管理:

 void* malloc(size_t size)

 void* calloc(size_t nobj, size_t size)

 void* realloc(void *p, size_t size)

 void* free(void *p)

上面这四个函数,都返回一个void* 指针。在C中,void* 指针可以接收任意类型指针,同时,可以不经强制类型转换直接传递给任意类型指针。而在C++中,前一种情况一样,后一种情况必须进行强制类型转换。
先说说malloc,它接收一个size_t 类型的参数作为请求的内存的大小,以字节为单位。所以,在内存分配中,通常会用到sizeof运算符来获取要分配的数据类型的大小。如:

int *p = malloc(sizeof(int));

为一个int类型的指针 p 分配了一段内存区域。
calloc函数,用于为一个数组对象分配内存,第一个参数为数组的大小,第二个参数为每中数据类型的大小。一般calloc用得不多,可以直接用malloc替代。
realloc函数,用于调整已分配内存的大小。当你发现你的当前内存太多或太小时,就可以用realloc来进行内存的调整了。
所有这三个内存分配函数失败时都会返回NULL,所以可以通过检查返回值来判断内存分配是否成功。
最后,free函数,用于释放使用上述三个函数动态分配的内存。并且必须进行释放。
C中的内存分配函数的实现是由系统来完成的。所以不同系统会有不同的实现。UNIX-like系统中,一般是由sbrk系统调用来实现的。(更具体?)
在C++中,内存管理主要用操作符new 和 delete。相对C来说更方便一些,并且效率更高。不过,有个地方我没想明白的是,为何STL中的内存管理要单独写一个allocator来实现。还是new和delete不够强大?嗯,毕竟不了解new 和 delete的实现。

转载请注明地址: http://www.qyspaces.com/?p=262

C程序的构成及动态内存分配的更多相关文章

  1. SQLite剖析之动态内存分配

    SQLite通过动态内存分配来获取各种对象(例如数据库连接和SQL预处理语句)所需内存.建立数据库文件的内存Cache.保存查询结果. 1.特性    SQLite内核和它的内存分配子系统提供以下特性 ...

  2. C和指针 第十一章 动态内存分配

    声明数组时,必须指定数组长度,才可以编译,但是如果需要在运行时,指定数组的长度的话,那么就需要动态的分配内存. C函数库stdlib.h提供了两个函数,malloc和free,分别用于执行动态内存分配 ...

  3. 动态内存分配导致Javascript性能的问题

    内存分配对性能的影响是很大的,分配内存本身需要时间,垃圾回收器回收内存也需要时间,所以应该尽量避免在堆里分配内存.不过直到最近优化HoLa cantk时,我才深刻的体会到内存分配对性能的影响,其中有一 ...

  4. C++动态内存分配

    C++动态内存分配1.堆内存分配 :C/C++定义了4个内存区间:代码区,全局变量与静态变量区,局部变量区即栈区,动态存储区,即堆(heap)区或自由存储区(free store). 堆的概念:通常定 ...

  5. C动态内存分配(C与指针实例)

    主要初步介绍malloc.free.calloc.realloc的基本.日后会有更详细的内容. malloc.free分别用于动态内存分配和释放. malloc会从内存池里提取一块合适的内存(连续的) ...

  6. C++学习笔记(十一):void*指针、类型转换和动态内存分配

    void*指针 void关键字表示“空类型”的概念.但是,这里的“空类型”不表示“任意类型”,而是表示不存在的意思,也就是说C/C++不允许你写语句void a,不存在类型为void的东西. void ...

  7. 转: Linux C 动态内存分配 malloc及相关内容 .

    一.malloc()和free()的基本概念以及基本用法: 1.函数原型及说明: void *malloc(long NumBytes):该函数分配了NumBytes个字节,并返回了指向这块内存的指针 ...

  8. 动态内存分配(new)和释放(delete)

    在之前我们所写过的程序中,所必需的内存空间的大小都是在程序执行之前就已经确定了.但如果我们需要内存大小为一个变量,其数值只有在程序运行时 (runtime)才能确定,例如有些情况下我们需要根据用户输入 ...

  9. iOS开发——C篇&动态内存分配

    再C语言中关于内存是一个很重要的知识点,所以今天我就从c语言的内存分配开始为大家解析一下C语言再iOS开发中非常重要的一些知识. 1:malloc函数的介绍 C语言中开辟内存空间:malloc函数 再 ...

随机推荐

  1. IP头部校验(转)

    一:原理 当发送IP包时,需要计算IP报头的校验和: 1.把校验和字段置为0: 2.对IP头部中的每16bit进行二进制求和: 3.如果和的高16bit不为0,则将和的高16bit和低16bit反复相 ...

  2. 问题-Delphi编译时提示缺少delphi自己的单元文件

    问题现象:在编译工程是,提示缺少DELPHI自己的很多单元. 问题原因:这可能是因为手动误删除,或是第三方控件安装时误删除DELPHI自己的目录引起的(如果说错了,希望高人指点). 问题处理: 方法一 ...

  3. [html]js打开指定页面

    1.在当前窗口打开 location.href = "http://www.baidu.com"; 2.可以设置开发方式 window.open("http://www. ...

  4. oc学习之路----scrollView的代理模式

    右图是OC里面scrollView的代理的描述,从这里可以开出来,任何对象都可以作为scorllView的代理对象只要实现了UIScrollViewDelegate这个协议,为什么呢,原因要追究到UI ...

  5. 大数据与可靠性会碰撞出什么样的Spark?

    可靠性工程领域的可靠性评估,可靠性仿真计算,健康检测与预管理(PHM)技术,可靠性试验,都需要大规模数据来进行支撑才能产生好的效果,以往这些数据都是不全并且收集困难,而随着互联网+的大数据时代的来临, ...

  6. 动态添加DOM时,绑定的click事件会重复执行

    最近因为业务需求,需要重写window的alert和confirm弹窗,但是每次显示的提示按钮不相同,所有每次打开的弹窗都需要重写生成,但是对于相同的按钮会保留上次创建时的click事件,所以当你创建 ...

  7. Play!framework 项目部署到Tomcat

    Play Framework有自带的服务器,也可部署到其他服务器上.这里讲解下如何将Play的项目部署到Tomcat. 1.准备war包 首先进入play目录: 比如我的: cd C:\play-1. ...

  8. WindDbug应用

    Windbg是windows平台上的一款相当强大的调试工具,可以从msdn网站下载得到,最新版本包含在windows sdk中,默认会被安装在C:\Program Files\Debugging To ...

  9. quartz源码分析之深刻理解job,sheduler,calendar,trigger及listener之间的关系

    org.quartz包 包org.quartz是Quartz的主包,包含了客户端接口. 其中接口有: Calendar接口: 定义了一个关联Trigger可能(或者不可能)触发的时间空间.它没有定义触 ...

  10. Building and setting up QT environment for BeagleBone

    There are too few information available on how to easily setup QT environment for building Beaglebon ...