创建退出功能

时间:2018-03-23 19:48:27

标签: c malloc

我有学校项目,我们正在使用malloc并且免费。

这些是我们可以使用的授权函数:malloc,free,read和getline。

我想在程序中验证对malloc的所有调用,但这样做很烦人,我不知道如何正确地执行。

我正在创建一个函数xmalloc,当malloc返回NULL时使用exit,但因为我们不能再使用exit函数了。

void *xmalloc(size_t size)
{
    void *data = malloc(size);
    if (!data)
        exit(84);
    else
        return data;
}

如何正确验证程序中的功能?

1 个答案:

答案 0 :(得分:3)

手头有两种可能性。一个是好的,一个是非常非常糟糕的。

很可能是您的教师可能希望您探索如何通过多级函数调用将错误情况渗透回原始调用者。这很有用,没有什么魔力:只需要检查错误,并小心细节。

很可能是你的导师是个白痴,他认为检查malloc()&#34等函数的返回值很重要,因为他们通常不会失败" 。这是现在令人惊讶的常见态度,并导致程序崩溃而没有任何警告(只是一个分段错误类型的错误),例如内存或CPU时间限制(进程限制)正在使用中。这只是愚蠢的。

在后一种情况下,你的错误和中止 - 如果 - malloc() - 失败帮助函数是更好的选择:至少用户可以检查退出状态(在Bash,echo $?)找到一些关于为什么程序会自行解决的线索,而不是想知道刚刚发生了什么。

希望这是令人惊讶的前一种情况 - 如何正确地将错误案件传递给来电者 - 我会继续。

首先,简短的背景知识为什么这对我们来说真的很有用。

假设你有一个库,例如一个进行色彩空间转换的库。 (JPEG图像和大多数电影格式使用YUV或类似的色彩空间,而大多数计算机显示器使用RGB。新的渲染软件可能支持HDR,高动态范围,其中更多位用于表示每个颜色分量而不是正常。所以,这是一个共同需要。)

如果可表示颜色的总数相对于可用内存的大小较小,则库可能只是预先计算从源颜色空间到目标颜色空间的所有可能颜色的转换。例如,对于16位RGB / YUV,这只需要131072字节或128 kiB的内存;对于24位,50331648字节或48 MiB。

而不是试图找出,程序/库可能有一个全局设置(在速度和节俭内存使用之间,比如五个步骤),如果足够高,尝试 {{1} };否则会回退到缓慢,缓存甚至没有查找表的功能。

因此,存在内存分配失败不是终端的情况。

我已经涉足了一个C矩阵库,其中矩阵数据被引用计数并自动管理;不同的矩阵甚至可以是其他矩阵的实时视图。内容。所有用户/程序员需要做的就是记住" drop"在它们不再需要它之后的矩阵,并且存储器由库管理。

在该库中,可以推迟内存丢弃,直到明确询问,发生足够大的分配,或者分配失败(在这种情况下,释放当前未使用的数据,并重新分配分配)。这是为了让用户/程序员在有大量可用内存的情况下最大限度地提高速度,同时还要使用#34;清理笔记"每当代码执行大量磁盘I / O时,在家务管理中花费一些CPU周期基本上是免费的。

简而言之,掌握如何处理错误以及如何将错误传播回调用者是非常有用的。特别是,有时能够指出错误的类型或原因很重要,因此呼叫者可以就如何继续做出明智的决定。

在我们开始之前,我们需要了解一些基本事实:

  • 因为通常只有一种方法可以成功,但很多方法都会失败,因此使用malloc()表示成功非常有用,而使用非零表示失败代码则非常有用。 通常,函数返回值可用于报告失败代码(如果成功则为零)。在这种情况下,如果呼叫失败,则测试0为真;如果呼叫成功,则测试(func(..))为真。
    如果使用平台声明的(!func(...))常量,也可以使用errno中的<errno.h>来保存错误代码。然后,您可以使用E...中的strerror(code)来获取描述错误的字符串。

  • <string.h>是安全的,什么都不做。
    这意味着如果我们将用于动态分配数据的指针初始化为free(NULL),则在错误路径中我们只能NULL它们。如果他们尚未分配,则不会造成任何伤害,因为free()是安全且无害的。

  • free(NULL)malloc(size)完全相同。 我很少使用realloc(NULL, size),但经常使用malloc()。结合上面的内容,当我知道我需要在某个数组中动态分配至少realloc()个字符时,我可以调用size来调整它的大小,无论是否已经动态分配了某些内存。
    这也与realloc()等POSIX.1函数兼容。不需要getline()初始行缓冲区;只需将缓冲区初始化为malloc()并将大小分配给NULL,第一个0调用将在必要时动态分配新缓冲区。

  • getline()返回三件事之一。
    如果它返回realloc(oldptr, newsize),则NULL的内容仍然不变; oldptr 已释放。
    通常,呼叫在成功时返回oldptr
    该调用可能会返回一个与oldptr不同的全新指针。在这种情况下,所有数据(从oldptr到旧分配的大小)都会自动复制到返回指针所指向的区域。
    实际效果是,当指向结构的旧位置的指针可能存在时,您不希望oldptr链接列表或树节点或任何其他数据结构。这是一个&#34;陷阱&#34;有时可能会咬新程序员,因为他们不知道realloc()可能(但通常不会)移动内存区域。

  • 清理时,realloc()来电的顺序不必与其分配顺序相同。 我个人喜欢以相反的顺序这样做,但原因是它有助于我理解对称性:对我而言,它就像保持大括号free(){一样...它也可能对你有帮助,或者它可能不值得努力。
    (它实际上可能对大多数典型的C库分配器有所帮助,但它都是挥手,并不值得担心。当你进展到这一点时,试验可能是一件有趣的事情。您对内存碎片和缓存效果感兴趣;但即便如此,每个C库甚至库版本的行为都可能不同。)

让我们考虑一个函数需要两个大小为}SIZE1的临时动态分配数组的情况,并且它调用的内部辅助函数使用一个额外的动态数组,大小SIZE2

SIZE3

我使用返回值static int helper(/* arguments omitted */) { char *temp; temp = malloc(SIZE3); if (!temp) return ENOMEM; /* Do stuff */ free(temp); return 0; } int some_work(/* parameters omitted */) { int *data1 = NULL; double *data2 = NULL; int result; data1 = malloc(SIZE1 * sizeof data1[0]); data2 = malloc(SIZE2 * sizeof data2[0]); if (!data1 || !data2) { free(data2); free(data1); return ENOMEM; } /* Do some work */ /* We might even discard one of them here, if we know it won't be needed anymore. To keep the cleanup simple, we just NULLify the variable if we free() it. */ if (somecondition) { free(data1); data1 = NULL; } /* Do some further work */ result = helper(); if (result) { free(data1); free(data2); return result; } /* Do some more work */ /* Done. Success. */ free(data1); free(data2); return 0; } 表示成功,非零值(目前只有0)表示失败或错误。

ENOMEM函数限定符(用于内部帮助函数)意味着它只在此编译单元(源文件)中可见,并且不能从外部访问。如果您不希望从外部调用内部库函数,那么它在库中尤其有用。

因为辅助函数在函数开始时对static进行了无条件赋值,所以无需将其初始化为temp

(如果您发现任何不清楚的部分,请在评论中告诉我,我会尝试找到更好的措辞,或构建一个更好的例子。)