原文:深入理解C指针之二:C内存管理

  内存管理对所有程序来说都很重要。有时候内存由运行时系统隐式的管理,比如为变量自动分配内存。在这种情况下,变量分配在它所处的函数的栈帧上(每个函数都有它自己的栈帧,用来保存它的局部变量和返回地址等)。如果是静态或全局变量,内存处于程序的数据段,会被自动清零。数据段是一个区别于可执行代码和运行时系统管理的其它数据的内存区域。

  C语言也支持动态内存管理,对象就是从堆上分配出来的内存。这是用分配和释放函数手动实现的,这个过程被称为动态内存管理。在C中动态分配内存的基本步骤有:

  1、用malloc类的函数分配内存;

  2、用这些内存支持应用程序;

  3、用free函数释放内存。

int* pi = (int*) malloc (sizeof(int));
*pi = 5;
printf(" pi is : %d\n address : %p\n", *pi, pi);
free(pi);
// pi is : 5
// address : 0x8843008

  malloc函数的参数指定要分配的字节数。如果成功则返回从堆上分配的内存的指针,如果失败则返回空指针。sizeof操作符能确定在宿主系统中应该分配的正确字节数,因为在不同的系统中,数据的字节数可能是不同的。每次调用malloc或类似函数时,结尾都应有对应的free函数调用,以防止内存泄露。

  如果不再使用已分配的内存却没有将其释放就会发生内存泄露,导致内存泄露的情况可能如下:

  1、丢失内存地址;

  2、应该调用free函数却没有调用(隐式泄露);

  以下几个内存分配函数可以用来管理动态内存,大部分包含在stdlib.h头文件中:

  1、malloc:从堆上分配内存

  2、realloc:在之前分配的内存块的基础上,将内存重新分配为更大或更小的部分

  3、calloc:从堆上分配内存并清零

  4、free:将内存块返回堆

  动态内存从堆上分配,对于一连串内存分配调用,系统不保证内存的顺序和所分配内存的连续性。不过,分配的内存会根据指针的数据类型对齐,比如4字节的整数会分配在能被4整除的地址边界上。堆管理器返回的地址是最低字节的地址。

  malloc函数从堆上分配一块内存,分配的字节数由唯一的参数制定,返回值是void指针,如果内存不足,则返回NULL。此函数不会清空或修改内存,所以新分配的内存包含垃圾数据。

void* malloc(size_t);

  执行malloc函数会执行以下操作:

  1、从堆上分配内存;

  2、内存不会被修改或者清空;

  3、返回首字节的地址;

  void指针可以转换成任意指针,但是显示的指针类型转换能获得对c++的更好兼容性,也能使代码更清晰。注意函数参数尽量使用sizeof操作符,而且不要试图接引一个未初始化内存数据的指针变量。

int* mpi = (int*) malloc (sizeof(int));
if(mpi!=NULL)
{
//pointer OK
}
else
{
//pointer not OK
}

 初始化静态或全局变量时不能调用动态内存分配函数。但是对于静态变量,可以用一个单独的赋值语句来给变量分配内存。

static int* spi;
spi = (int*) malloc (sizeof(int));

 使用calloc函数会在分配的同时清空内存。清空内存意思是将其内容置为二进制的0。

void* calloc(size_t numElements, size_t elementSize);

 calloc函数根据两个参数的乘积来分配内存,并返回指向内存的第一个字节的指针。如果不能分配内存,则会返回NULL。执行calloc可能比malloc慢,因为它要做额外的内存清理工作。

 如果需要增加或减少为指针分配的内存,可以使用realloc函数

void* realloc(void* ptr, size_t size);

  第一个参数是指向原内存快的指针,第二个是请求的大小。返回值是指向重新分配的内存的指针。如果请求的新内存大小比原来的小,多余的内存会返还给堆。如果比原来的大,可能的话,会紧挨着当前分配的内存区域分配新的内存,否则会在堆的其它区域分配并把就的内存复制到新区域。如果请求大小是0,那么就释放内存。如果无法分配,则返回空指针。注意永远不要使用超出内存分配的地址。

  使用free函数将不再使用的内存返还给系统。

void free(void* ptr);

  指针参数应该指向由malloc函数分配的内存,这块内存会被返还给堆。尽管该指针仍然指向该内存,但是该内存现在被认为包含垃圾数据,并且可能被写入新的数据。该指针则称为迷途指针。注意空指针不指向任何东西,迷途指针还是指向一个地址。如果是在一个函数里面分配的内存,那么就应该在同一个函数里面释放它。

    重复释放是指两次释放同一块内存。第二次调用free会引发运行时异常。两个指针指向同一个内存地址称为别名。别名可能导致重复释放。堆管理器不一定会在free函数后将内存返回给操作系统,而是可能给程序后续使用。

    迷途指针是指访问已释放的内存的指针。使用迷途指针会导致不可预期的行为和潜在的安全隐患。在指针别名的情况下,迷途指针更难以察觉。

int *gpi;
{
int tmp = 5;
*gpi = &tmp;
}

    大部分编译器把块语句当做一个栈帧。在块语句退出是,分配在栈帧上的tmp会出栈,gpi变成迷途指针,指向一块已经被自动回收的内存。

    除了使用函数手动分配内存外,还有一些非标准技术可以用来实现C的动态内存管理。这些技术的关键特性在于自动释放内存。内存不再使用之后会被收集起来备用,释放的内存成为垃圾,因此这个过程也叫垃圾回收。垃圾回收使程序员不必费心考虑何时释放内存,更专注程序本身的问题。垃圾回收技术包括RAII和异常处理等。

深入理解C指针之二:C内存管理的更多相关文章

  1. 03深入理解C指针之---变量与内存

    该系列文章源于<深入理解C指针>的阅读与理解,由于本人的见识和知识的欠缺可能有误,还望大家批评指教. C语言是一种编译型的语言,C源代码在编译成可执行文件后,经常以以下三种方式使用内存: ...

  2. Block介绍(二)内存管理与其他特性

    我们在前一章介绍了block的用法,而正确使用block必须要求正确理解block的内存管理问题.这一章,我们只陈述结果而不追寻原因,我们将在下一章深入其原因. 一.block放在哪里 我们针对不同情 ...

  3. iOS中Block介绍(二)内存管理与其他特性

    我们在前一章介绍了block的用法,而正确使用block必须要求正确理解block的内存管理问题.这一章,我们只陈述结果而不追寻原因,我们将在下一章深入其原因. 一.block放在哪里 我们针对不同情 ...

  4. 《Linux内核设计与实现》读书笔记(十二)- 内存管理【转】

    转自:http://www.cnblogs.com/wang_yb/archive/2013/05/23/3095907.html 内核的内存使用不像用户空间那样随意,内核的内存出现错误时也只有靠自己 ...

  5. # 深入理解Redis(二)——内存管理的建议与技巧

    引语 随着使用Redis的深入,我们不可避免的需要深入了解优化Redis的内存,本章将重点讲解Redis的内存优化之道,同时推荐大家阅读memory-optimization一文. 想要高效的使用Re ...

  6. c++ boost库学习二:内存管理->智能指针

    写过C++的人都知道申请和释放内存组合new/delete,但同时很多人也会在写程序的时候忘记释放内存导致内存泄漏.如下所示: int _tmain(int argc, _TCHAR* argv[]) ...

  7. Block(二)内存管理与其他特性

    一.block放在哪里 我们针对不同情况来讨论block的存放位置: 1.栈和堆 以下情况中的block位于堆中: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 ...

  8. Block(二)内存管理与其他特性-b

    一.block放在哪里 我们针对不同情况来讨论block的存放位置: 1.栈和堆 以下情况中的block位于堆中: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 ...

  9. MySQL 调优基础(二) Linux内存管理

    进程的运行,必须使用内存.下图是Linux中进程中的内存的分布图: 其中最重要的 heap segment 和 stack segment.其它内存段基本是大小固定的.注意stack是向低地址增长的, ...

随机推荐

  1. bzoj 3333: 排队计划 解决问题的方法

    [原标题] 3333: 排队计划 Time Limit: 20 Sec  Memory Limit: 128 MB Submit: 161  Solved: 71 [Submit][Status] D ...

  2. StackExchange.Redis 使用 - 事件(五)

    ConnectionMultiplexer 可以注册如下事件 ConfigurationChanged - 配置更改时 ConfigurationChangedBroadcast - 通过发布订阅更新 ...

  3. SQL Server 移动数据库

    原文:SQL Server 移动数据库 移动系统数据库在下列情况下可能很有用: 故障恢复.例如,数据库处于可疑模式下或因硬件故障而关闭. 计划的重定位. 为预定的磁盘维护操作而进行的重定位. 下列过程 ...

  4. cocos2dx --- button点击放大中心

    自定义简单button,直接附着到代码: MenuItem* MenuItemNode::create( const char* normal,Ref* target,SEL_MenuHandler ...

  5. 基于VMware的虚拟Linux集群搭建-lvs+keepalived

    基于VMware的虚拟Linux集群搭建-lvs+keepalived 本文通过keepalived实现lvsserver的的双机热备和真实server之间的负载均衡.这方面的blog挺多,可是每一个 ...

  6. .NET单元测试艺术(3) - 使用桩对象接触依赖

    List 3.1 抽取一个设计文件系统的类,并调用它 [Test] public bool IsValidLogFileName(string fileName) { FileExtensionMan ...

  7. matlab入门 蜂窝阵列

    B{1,1}=[1,2]; B{1,2}='kitty'; B B = [1x2 double]    'kitty' celldisp(B) B{1} =      1     2 B{2} =   ...

  8. 一个好用的web甘特图

      前些天一直在弄web甘特图,发现网上很多web甘特图框架,但大部分是收费的.偶尔发现了向日葵甘特图 感觉不错,特此写下来一方面当做记录,另一方面也为寻找web甘特图的同学们少走一些弯路,双赢嘛~ ...

  9. 恢复SQLSERVER被误删除的数据

    原文:恢复SQLSERVER被误删除的数据 恢复SQLSERVER被误删除的数据 曾经想实现Log Explorer for SQL Server的功能,利用ldf里面的日志来还原误删除的数据 这里有 ...

  10. SendRedirect和forward差分

    (1)重定向JSP实现JSP/Servlet跳转到目标资源的方法中,基本的想法是:server目标资源完成URL通过HTTP 在回答本报发client浏览器.收到的浏览器URL更新到地址栏后,而目标资 ...