malloc()函数(Linux程序员手册)及函数的正确使用【转】
转自:https://blog.csdn.net/david_xtd/article/details/7311204
名称
malloc,free,calloc,realloc--分配和释放动态内存
概要
#include <stdlib.h>
void *malloc(size_tsize);
void free(void *ptr);
void *realloc(void*ptr, size_t size);
void *calloc(size_tnmemb, size_tsize);
描述
malloc()函数分配size个字节的内存并返回指向已分配内存的指针。该内存未初始化。如果size为0,malloc()或者返回空指针NULL,或者返回能成功传给free()函数的唯一的指针值。
free()函数释放指针ptr指向的内存空间,ptr必须是调用malloc()、calloc()或realloc()时返回的指针值,否则,或者如果free(ptr)已经被调用过了,会发生不确定的行为。如果ptr是空指针NULL,则不会执行任何操作。
calloc()函数为一个nmemb个元素大小为size个字节的数组分配内存并返回指向已分配内存的指针。已分配内存的内容被清零。如果nmemb或size为0,calloc()或者返回空指针NULL,或者返回能成功传给free()函数的唯一的指针值。
realloc()函数改变ptr指向的内存块的大小为size个字节。从内存块的起始地址到新旧大小最小值之间区域的内容不变。如果新的内存块大于原来的内存块,新增加的内存未被初始化。如果ptr是空指针NULL,对于所有size来说,该调用就等效于malloc(size);如果size为0,并且ptr是非空指针NULL,则该调用等效于free(ptr)。只要ptr是非空指针NULL,则必须是之前调用malloc()、calloc()或realloc()时返回的指针值。如果指向的区域移动了,会进行free(ptr)操作。
返回值
malloc()函数和calloc()函数返回指向已分配区域的指针。该内存对于任何类型的变量都已经合理地对齐。出错时,返回空指针NULL。空指针也可能在成功调用当size为0的malloc()函数时返回,或成功调用nememb为0或size为0的calloc()函数时返回。
free()函数没有返回值。
realloc()函数返回指向新分配内存区的指针,该内存区已对于任何类型的变量已合理对齐,该指针可能与ptr不同,或者当请求失败时返回空指针NULL。如果size为0,或者返回空指针NULL,或者返回要传给free()函数的合适的指针。如果realloc()函数失败,原来的内存块不受影响,既未移动,也未释放。
注释
通常,malloc()函数自堆中分配内存,并使用sbrk根据需求调整堆的大小。当正在分配的内存块大于MMAP_THRESHOLD字节数时,glibc的malloc()实现使用mmap将内存的分配当作私有的匿名映射。MMAP_THRESHOLD缺省是128KB,但可以使用mallopt调整大小。使用mmap进行的内存分配不受RLIMIT_DATA资源限制的影响。
UNIX 98标准要求malloc()、calloc()和realloc()在出错时设定errno为ENOMEM。Glibc假定这些要求都已经实现。如果用户使用了没有设定errno的私有的malloc实现,某些特定的库函数在errno上可能会毫无理由地失败。
英文网址:http://www.kernel.org/doc/man-pages/online/pages/man3/free.3.html
使用注意事项
1. 函数使用中需要注意:
A、申请了内存空间后,必须检查是否分配成功。
B、当不需要再使用申请的内存时,记得释放;释放后应该把指向这块内存的指针指向NULL,防止后面程序不小心使用了它。
C、这两个函数应该是配对的。如果申请后不释放就是内存泄露;如果无故释放那就是什么也没有做。只能释放一次,如果释放两次及两次以上会
出现错误(释放空指针例外,释放空指针其实也等于啥也没做,所以释放空指针释放多少次都没有问题)。
D、虽然malloc()函数的类型是(void *),任何类型的指针都可以转换成(void *),但是最好还是在前面进行强制类型转换,因为这样可以躲过一
些编译器的检查。
2. malloc()从堆得到内存空间。
堆是大家共有的空间(分全局堆和局部堆。全局堆就是所有没有分配的空间,局部堆就是用户分配的空间)。堆在操作系统对进程 初始化的时候分配,运行过程中也可以向系统要额外的堆,但是记得用完了要还给操作系统,要不然就是内存泄漏。
与之对应的是栈。栈是线程独有的,保存其运行状态和局部自动变量的。栈在线程开始的时候初始化,每个线程的栈互相独立。每个函数都有自己的栈,栈被用来在函数之间传递参数。操作系统在切换线程的时候会自动的切换栈,就是切换SS/ESP寄存器。栈空间不需要在高级语言里面显式的分配和释放。
通过上述内容,malloc()和free()只会涉及到堆的使用,可由用户控制;(为防止内存泄露,malloc()使用结束一定要记得释放!!)
而栈的使用是由系统自动控制的,无需用户介入。
3. malloc()和free()的机制
free()函数非常简单,只有一个参数,只要把指向申请空间的指针传递给free()中的参数就可以完成释放工作。
这里要追踪到malloc()的申请问题了。申请的时候实际上占用的内存要比申请的大。因为超出的空间是用来记录对这块内存的管理信息。
malloc()申请的空间实际我觉得就是分了两个不同性质的空间。一个就是用来记录管理信息的空间,另外一个就是可用空间了。而用来记录管理信息的实际上是一个结构体。
在C语言中,用结构体来记录同一个对象的不同信息是天经地义的事!下面看看这个结构体的原型:
struct mem_control_block {
int is_available; //这是一个标记?
int size; //这是实际空间的大小
};
对于size,这个是实际分配空间大小。
下面看看free()的源代码。
程序代码:
- // code...
- void free(void *ptr) {
- struct mem_control_block *free;
- free = ptr - sizeof(struct mem_control_block);
- free->is_available = 1;
- return;
- }
看一下函数第二句,这句非常重要和关键。其实这句就是把指向可用空间的指针倒回去,让它指向管理信息的那块空间,因为这里是在值上减去了一个结构体的大小!
图示如下:
该文作者原先有错误的认识,就是认为指向那块内存的指针不管移到那块内存中的哪个位置都可以释放那块内存!
但是,这是大错特错!释放是不可以释放一部分的!首先这点应该要明白。而且,从free()的源代码看,ptr只能指向可用空间的首地址,不然,减去结构体大小之后一定不是指向管理信息空间的首地址。所以,要确保指针指向可用空间的首地址!
该错误认识通过上图更能清晰地看出来,只有当ptr指向可用空间的首地址时,在ptr减去struct mem_control_block的大小后才能使得后面的语句free->is_available得到有效数值。否则,传给系统的数值就肯定不是正确的,由此造成的后果也不可预测了。
4. 错误使用举例。
int main(int argc, char *argv[]) { char *buffer = NULL, *str = NULL; char *a = "abcdefghij"; int length = 10, i = 0; buffer = (char *)malloc(length * sizeof(char)); if(!buffer) { printf("1:malloc error!\n"); return 0; } memset(buffer, 0, length); for(i = 0; i < length - 1; i++) { *buffer++ = a[i]; } fprintf(stderr, "buffer: %s\n", buffer); free(buffer); str=(char *)malloc(length * sizeof(char)); if(!str) { printf("2:malloc error!\n"); return 0; } memset(str, 0, length); for(i = 0; i < length - 1; i++) { str[i] = a[i]; } fprintf(stderr, "str: %s\n", str); free(str); }上面程序中同样使用malloc()函数为buffer和str分配了10个字节的内存,最后都通过free()的方式释放内存。其中,malloc(str)和free(str)都是正确的,而malloc(buffer)和free(buffer)就错了。因为指针buffer的值发生改变,已经不再是通过malloc(buffer)返回的指针值了。
总之,一句话,为防止内存泄露,malloc()使用结束一定要记得用free(ptr)释放!!
并且,ptr一定要是malloc()分配时得到的指针地址。
malloc()和free()机制等部分转载了bccn作者ID:lj_860603(键键)的文章,如下:
http://www.bccn.net/Article/kfyy/cyy/jszl/200608/4238_2.html
malloc()函数(Linux程序员手册)及函数的正确使用【转】的更多相关文章
- DOS程序员手册(一)
当今MS-Windows横扫大江南北,让我们这就来研究一下它的祖宗——MS-DOS! 这本书很难得,希望读者好好学习! DOS程序员手册(一) DOS教程 (以下内容全部为原作者的阐述,照样保留) 这 ...
- PowerShell:Linux程序员喜欢的cmd增强版
Linux程序员有时偶尔使用Windows下的cmd工具,会被逼疯的,有些命令ls, cat, ps等已经条件反射一样使用. 但在cmd下,根本不知道该用什么命令,好在盖兹大叔照顾了此部分需求.从Vi ...
- 函数sql黑马程序员——SQL常用函数
最近使用开辟的过程中出现了一个小问题,顺便记录一下原因和方法--函数sql ---------------------- ASP.Net+Android+IO开辟S..Net培训.等待与您交流! -- ...
- 做10年Windows程序员与做10年Linux程序员的区别
如果一个程序员从来没有在linux,unix下开发过程序,一直在windows下面开发程序, 同样是工作10年, 大部分情况下与在linux,unix下面开发10年的程序员水平会差别很大.我写这篇文章 ...
- 做10年Windows程序员与做10年Linux程序员的区别(附无数评论)(开源软件相当于熟读唐诗三百首,不会作诗也会吟)
如果一个程序员从来没有在linux,unix下开发过程序,一直在windows下面开发程序, 同样是工作10年, 大部分情况下与在linux,unix下面开发10年的程序员水平会差别很大.我写这篇文章 ...
- DOS程序员手册(九)
第14章参考手册概述 本书余下的章节将向读者们介绍BIOS.DOS各种各样API函数和服务,作为一名程 序员,了解和掌握这些知识是很有好处的.在所介绍的参考手册中,每部手册都汇集了大 量的资源 ...
- DOS程序员手册(八)
备,就可以从程序中访问驱动程序.可以用句柄功能调用来 打开设备(见列表12.9) 列表12.9 /*example.C List ...
- DOS程序员手册(七)
第11章 中断处理程序 本章将深入到DOS系统内部探讨中断处理程序的内容.与其他计算机编程不一样, 中断处理程序这个名词听起来就很难懂.用最简单的话来说,中断处理程序就是对应于中 断激活的程 ...
- DOS程序员手册(六)
217页 程序的主要部分后面是主程序所使用的许多小的扩充内存功能.将这些功能组合起 来这些功能便覆盖了扩充内存的操作,尽管还可能想向它们添加错误检查. 程序所包含的函数有: emmtest 检验内 ...
随机推荐
- 述 SQL 中的 distinct 和 row_number() over() 的区别及用法
1 前言 在咱们编写 SQL 语句操作数据库中的数据的时候,有可能会遇到一些不太爽的问题,例如对于同一字段拥有相同名称的记录,我们只需要显示一条,但实际上数据库中可能含有多条拥有相同名称的记录,从而在 ...
- 【BZOJ3811】玛里苟斯(线性基)
[BZOJ3811]玛里苟斯(线性基) 题面 BZOJ 题解 \(K=1\)很容易吧,拆位考虑贡献,所有存在的位出现的概率都是\(0.5\),所以答案就是所有数或起来的结果除二. \(K=2\)的情况 ...
- Java -- JDBC 学习--获取数据库链接
数据持久化 持久化(persistence): 把数据保存到可掉电式存储设备中以供之后使用.大多数情况下,特别是企业级应用,数据持久化意味着将内存中的数据保存到硬盘上加以”固化”,而持久化的实现过程大 ...
- Spring的后置处理器BeanFactoryPostProcessor
新建一个JavaBean UserBeanFactoryPostProcessor 实现了BeanFactoryPostProcessor接口 Spring配置文件如下: 编写测试用例 从结果可以看出 ...
- spread与react
我们写react组件的过程中会遇到这个 我们知道react中的{},浏览器会知道说是以js的形式进行解析出来.那么怎么解析...props呢? 这个就涉及到es6中的扩展运算符了,我们先看下面的一些运 ...
- 开启 Hyper-v 后如何使用 Android Emulator?
如果开启了 Hyper-v 时,当需要使用 Android Studio 中 Android Emulator 时,系统会出现蓝屏代码错误. 使用下面的方法,则可以解决冲突. 首先,你需要确保已经开启 ...
- gcc/g++
$gcc -g -Wall -ansi -pedantic main.cpp -lstdc++ -std=c++11 -lpthread -o xmain
- Java如何实现跨平台
在前面讲解编程语言的时候我们看到,通过引入编译器,解决了使用机器语言编程带来的问题.但这有待来了另一个问题:不同的平台(你可以理解成CPU不同.操作系统不同)所能理解的二进制机器指令是不一样的,编译器 ...
- java常见面试题及三大框架面试
Java基础方面: 1.作用域public,private,protected,以及不写时的区别 答:区别如下: 作用域 当前类 同一package 子孙类 其他package public √ √ ...
- Js中this机制全解
JavaScript中有很多令人困惑的地方,或者叫做机制. 但是,就是这些东西让JavaScript显得那么美好而与众不同. 比方说函数也是对 象.闭包.原型链继承等等,而这其中就包括颇让人费解的th ...