原文:深入理解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. 关于WCF的引用,添加服务和添加web服务的区别

    原文:关于WCF的引用,添加服务和添加web服务的区别 本章内容主要是根据我做的实验来阐述这2种添加服务针对WCF的不同之处,我们按照示例一步一步来看. 如下是工程的结构: 该WCF服务是通过控制台程 ...

  2. Zen Coding 快速编写HTML/CSS代码的实现

    在本文中我们将展示一种新的使用仿CSS选择器的语法来快速开发HTML和CSS的方法.它由Sergey Chikuyonok开发. 你在写HTML代码(包括所有标签.属性.引用.大括号等)上花费多少时间 ...

  3. openSUSE 国内镜像摘要

    1. 北交(BJTU): http://mirror.bjtu.edu.cn/opensuse http://mirror.bjtu.edu.cn/packman/suse 2. 华中科大(HUST) ...

  4. JBoss配置解决高并发连接异常问题(转)

    这两天一个项目在做压力测试的时候,发现只要并发数超过250个,连续测试两轮就会有连接异常出现,测试轮数越多出现越频繁,异常日志如下: Caused by: com.caucho.hessian.cli ...

  5. java main方法背后的故事?(转)

    jvm java 看似一种语言,实则一个巨大的体系的王国,开发这么多年了,还是没有搞懂,我以为我懂了,可是过了一段时间又忘了,所以说还是没懂 1.main方法说起 编译完我们的java文件后,需要有个 ...

  6. Hadoop Mapreduce刮

    前言 的一个渣渣程序猿一枚,因为个人工作,须要常常和hadoop打交道,可是自己之前没有接触过hadoop.所以算是边学边用,这个博客算是记录一下学习历程,梳理一下自己的思路,请各位看官轻拍.本博客大 ...

  7. Codeforces Round #261 (Div. 2) E. Pashmak and Graph DP

    http://codeforces.com/contest/459/problem/E 不明确的是我的代码为啥AC不了,我的是记录we[i]以i为结尾的点的最大权值得边,然后wa在第35  36组数据 ...

  8. Unity3d 实时折射和反射

    这里只是张贴在实时折射和脚本反思shader, 大约NGUI第一部分请下载. 这个版本的主要缺点是折射平面部Layer必须是water层.假设有专家谁可以摆脱这一个.请记得把代码回该条,谢谢! Wat ...

  9. Android网络通信Volley框架源代码浅析(三)

    尊重原创 http://write.blog.csdn.net/postedit/26002961 通过前面浅析(一)和浅析(二)的分析.相信大家对于Volley有了初步的认识,可是假设想更深入的理解 ...

  10. 使用更清晰DebugLog开发和调试工具

    在开发和应用的开发和调试过程中难免会发现故障的过程中.我相信很多做iOS开发程序员Xcode的debug调试功能大加关注. 但在这样做Android开发过程中,却不那么方便,虽然IDE也提供了debu ...