gpt4 book ai didi

c - 为什么malloc有时不起作用?

转载 作者:可可西里 更新时间:2023-11-01 12:25:44 24 4
gpt4 key购买 nike

我正在将C项目从Linux移植到Windows。在Linux上,它是完全稳定的。在Windows上,大多数情况下都能正常运行,但是有时我遇到了段错误。

我正在使用Microsoft Visual Studio 2010进行编译和调试,看起来有时我的malloc调用根本不分配内存,返回NULL。机器有可用内存;它已经通过了该代码一千次,但仍然发生在不同的位置。

就像我说的,它不会一直或在同一地点发生;它看起来像一个随机错误。

在Windows上,我需要比在Linux上更加谨慎吗?我做错了什么?

最佳答案

malloc()无法为内存请求提供服务时,它将返回无效的NULL指针。在大多数情况下,C内存分配例程通过调用操作系统来管理内存可用内存的列表或堆,以在进行malloc()调用且列表或堆上没有块来满足请求时分配其他内存块。 。

因此malloc()失败的第一种情况是无法满足内存请求,因为(1)C运行时的列表或堆上没有可用的内存块,以及(2)C运行时内存管理请求更多的内存时从操作系统,该请求被拒绝。

这是有关Pointer Allocation Strategies的文章。

本论坛文章提供了malloc failure due to memory fragmentation的示例。
malloc()可能失败的另一个原因是,内存管理数据结构已损坏,可能是由于缓冲区溢出而导致的,在缓冲区溢出中,分配的内存区域用于大于分配的内存大小的对象。不同版本的malloc()可以使用不同的策略进行内存管理,并确定在调用malloc()时要提供多少内存。例如,malloc()可能会为您提供所请求的确切字节数,或者可能为您提供超出您要求的字节数,以便适合在内存边界内分配的块或使内存管理更轻松。

对于现代操作系统和虚拟内存,除非您要进行一些非常大的内存常驻存储,否则要用尽内存是非常困难的。但是,正如用户Yeow_Meng在下面的评论中提到的那样,如果您正在做算术以确定要分配的大小,并且结果为负数,则最终可能会请求大量的内存,因为malloc()的参数表示要分配的内存量未签名。

在执行指针算术以确定某些数据需要多少空间时,您可能会遇到负数大小的问题。对于非预期文本执行的文本解析,这种错误很常见。例如,以下代码将导致非常大的malloc()请求。

char pathText[64] = "./dir/prefix";  // a buffer of text with path using dot (.) for current dir
char *pFile = strrchr (pathText, '/'); // find last slash where the file name begins
char *pExt = strrchr (pathText, '.'); // looking for file extension

// at this point the programmer expected that
// - pFile points to the last slash in the path name
// - pExt point to the dot (.) in the file extension or NULL
// however with this data we instead have the following pointers because rather than
// an absolute path, it is a relative path
// - pFile points to the last slash in the path name
// - pExt point to the first dot (.) in the path name as there is no file extension
// the result is that rather than a non-NULL pExt value being larger than pFile,
// it is instead smaller for this specific data.
char *pNameNoExt;
if (pExt) { // this really should be if (pExt && pFile < pExt) {
// extension specified so allocate space just for the name, no extension
// allocate space for just the file name without the extension
// since pExt is less than pFile, we get a negative value which then becomes
// a really huge unsigned value.
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()的size参数可以大于要为其分配内存的对象所需的大小。因此,您可能需要对 malloc ()调用使用某种规则,以便通过舍入到一些标准内存量来分配标准大小的块。因此,您可以使用((size/16)+ 1)* 16或更可能的((size >> 4)+ 1)<< 4公式来分配16字节的块。许多脚本语言使用类似的东西来增加了重复调用 malloc()free()的机会,以使请求与列表或内存堆上的空闲块相匹配。

这是一个尝试减少分配和释放的块数的简单示例。可以说,我们有一个可变大小的内存块的链表。因此,链表中节点的结构如下所示:
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()。而且,如果您必须使用更大的缓冲区来更换缓冲区,则需要释放节点并重新分配缓冲区和节点。因此,程序员还有更多工作要做。

关于c - 为什么malloc有时不起作用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12433947/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com