malloc()
在无法满足内存请求时会返回一个无效的NULL指针。在大多数情况下,C语言的内存分配例程通过调用操作系统来分配更多的内存块或堆来管理可用内存列表或堆,以便在发生malloc()
调用且列表或堆上没有块可以满足请求时使用。
因此,malloc()
失败的第一种情况是由于(1) C运行时的可用内存列表或堆中没有可用的内存块,以及(2)当C运行时内存管理从操作系统请求更多内存时,请求被拒绝而不能满足内存请求。
这里有一篇关于指针分配策略的文章。
这篇论坛文章给出了由于内存碎片化而导致malloc失败的例子。
malloc()
可能失败的另一个原因是内存管理数据结构已经损坏,可能是由于缓冲区溢出,其中分配的内存区域用于比分配的内存大小更大的对象。不同版本的malloc()
可以使用不同的内存管理策略,并确定在调用malloc()
时提供多少内存。例如,malloc()
可能会精确地给你所请求的字节数,或者它可能会给你更多以适应分配的块在内存边界内或使内存管理更容易。
使用现代操作系统和虚拟内存,除非您正在进行某些真正大型的内存常驻存储,否则很难耗尽内存。然而,正如用户Yeow_Meng在下面的评论中提到的那样,如果您进行算术计算以确定要分配的大小,并且结果是负数,则可能会请求大量的内存,因为用于分配内存的参数是无符号的。
当进行指针算术运算以确定需要多少空间来存储一些数据时,您可能会遇到负大小的问题。这种错误通常发生在意外的文本解析中。例如,下面的代码将导致一个非常大的malloc()
请求。
char pathText[64] = "./dir/prefix";
char *pFile = strrchr (pathText, '/');
char *pExt = strrchr (pathText, '.');
char *pNameNoExt;
if (pExt) {
pNameNoExt = malloc ((pExt - pFile + 1) * sizeof(char));
} else {
pNameNoExt = malloc ((strlen(pFile) + 1) * sizeof(char));
}
一个良好的运行时内存管理将尝试合并已释放的内存块,从而将许多较小的块合并为更大的块。这些内存块的合并减少了使用 C 内存管理运行时管理的内存列表或堆上已有的内容无法满足内存请求的可能性。您可以通过尽量重用已分配的内存并尽量减少对malloc()和free()的依赖来提高内存管理效率。如果不使用malloc()函数,则很难出现失败情况。同时,您还可以通过将许多小的malloc()调用更改为较少的大型malloc()调用来降低内存碎片化和扩展内存列表或堆的大小的机会。此外,如果能够在同一时间连续地进行malloc()和free(),则内存管理运行时就可以更容易地合并内存块。因此,您可以在调用malloc()时使用一些规则,例如将分配的内存块大小舍入到某个标准内存块的大小。许多脚本语言都采用类似的方法以增加重复调用malloc()和free()函数时能够匹配请求与内存列表或堆上的空闲块的机会。例如,您可以按块大小为16个字节来分配内存块,使用一个公式((size / 16) + 1) * 16或者更可能是((size >> 4) + 1) << 4。最后,下面是一个简单的例子,在这个例子中,我们试图减少分配和释放的块的数量,我们假设有一个可变大小的内存块的链接列表。
typedef struct __MyNodeStruct {
struct __MyNodeStruct *pNext;
unsigned char *pMegaBuffer;
} MyNodeStruct;
对于特定缓冲区和其节点的内存分配,有两种方式。第一种是标准分配节点后按以下方式分配缓冲区。
MyNodeStruct *pNewNode = malloc(sizeof(MyNodeStruct));
if (pNewNode)
pNewNode->pMegaBuffer = malloc(15000);
不过另一种方法是采用以下方式,它使用单个内存分配和指针算术运算,以便单个malloc()
提供两个内存区域。
MyNodeStruct *pNewNode = malloc(sizeof(myNodeStruct) + 15000);
if (pNewNode)
pNewNode->pMegaBuffer = ((unsigned char *)pNewNode) + sizeof(myNodeStruct);
然而,如果您正在使用这种单一的分配方法,您需要确保在使用指针pMegaBuffer
时保持一致,不要意外地对其进行free()
操作。如果您需要用更大的缓冲区更换缓冲区,则需要释放节点并重新分配缓冲区和节点。因此,程序员需要进行更多的工作。
valgrind
运行程序了? - Jonathan Leffler