对一个程序,通常的理解就是,源码编译成机器代码,然后通过机器解释运行。不过是怎样编译成机器代码,和怎样运行的,无疑是个值得探讨的问题。怎样编译成机器代码,过程就是源码的编译、链接,编译器做了这些事。而怎样运行,却不是哪个器件自己一己之力就可以做到的。机器代码要在机器上运行,就得要请求硬件资源。涉及最多的就是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. elasticsearch安装过程中的license问题解决办法

    1.git clone git://github.com/mobz/elasticsearch-head.git 2.cd elasticsearch-head 3.npm install 出现下来问 ...

  2. [置顶] 程序员必知(三):一分钟知道URI编码(encodeURI)

    因为浏览器会用一些特殊的字符作为特定的意义,所以在要传输的内容上如果有这些特殊的字符的话,就需要对其进行转义才能正确传输,如以下字符为发送时候的关键字,即特殊字符 ;/?:@&=+$,# 所以 ...

  3. 20169210《Linux内核原理与分析》第十一周作业

    第17章 设备与模块 关于设备驱动和设备管理,讨论四种内核成分. 设备类型:在所有的linux系统中为了统一普遍设备的操作所分的类. 模块:Linux内核中用于按需加载和卸载目标码的机制. 内核对象: ...

  4. sharepoint 2013 更改搜索server组态

    1.新搜索server在.安装sharepoint server 2013,并连接到一个现有的sharepoint server领域,完成后.您可以配置新的搜索server. 打开sharepoint ...

  5. oracle数据库导入导出的dmp(转)

    window下: imp必须要dba用户,所以用sysdba用户登陆,然后给予chnlmgr用户dba权限 grant connect,resource,dba to chnlmgr; 全部导入 im ...

  6. android96 内存创建图片副本,画画板

    package com.itheima.copy; import android.os.Bundle; import android.app.Activity; import android.grap ...

  7. Qt全局热键(windows篇)

      Qt对于系统底层,一直没有很好的支持,例如串口并口通信,还有我们经常都会用到的全局热键,等等.既然Qt可能出于某种原因,不对这些进行支持,我们就只能自己写代码,调用系统相关的API了. 注意,这个 ...

  8. Android(java)学习笔记157:使用Dexdump等工具进行反编译

    使用Dex等工具进行反编译步骤: (1)首先找到Android软件安装包中的class.dex,把APK文件改名为".zip",然后解压缩其中的class.dex文件,这是Java ...

  9. arcgis10 安装1721错误

    arcgis10 安装1721错误,主要是ArcGIS License Manager 服务程序的位置不对,注册表lmgrd.exe中修改lmgrd.ex

  10. Java中的编码问题

    下面将侧重介绍java乱码是如何产生的.存在哪些乱码的情况.该如何从根本上解决乱码问题.各位随博主一起征服令人厌烦的java乱码问题吧!!! 一.Java编码转换过程 我们总是用一个java类文件和用 ...