【问题标题】:Memory allocation in CC中的内存分配
【发布时间】:2010-12-11 22:12:20
【问题描述】:

以下是ma​​lloc()的一个非常非常简单的版本,似乎给我分配了一些空间,但除了没有free()而且我不检查是否我已经超出了分配的空间,如何检查代码是否正确?

有什么明显的错误会让“C”专家扇我耳光吗?

#include <stdio.h>
#include <unistd.h>

#define MAX_MEMORY 1024 * 1024 * 2 /* 2MB of memory */

void *stack = NULL; /* pointer to available stack */
void * memoryAlloc(size) {
    if (stack == NULL)
        stack = sbrk(MAX_MEMORY); /* give us system memory */

    void *pointer;
    pointer = (void *)stack + size; /* we always have space :) */
    stack += size; /* move in stack forward as space allocated */
    return pointer;
}

【问题讨论】:

  • 我不会将内存领域称为“堆栈”。您当前的实现就像一个堆栈,是的,但是如果您希望它像 malloc 那样使用 free 工作,那么它就是一个堆。
  • 非常感谢你们所有的cmets!
  • 这个分配器是一个叫做“池分配器”的东西的开始——分配像上面那样发生,然后当使用池的工作单元完成时,整个块立即释放回系统。它有助于处理因必须单独管理每个小分配而导致的泄漏。 Apache 使用池 - HTTP 请求进来,为请求设置池,当请求完成时,池被释放。处理请求的其他任何事情都不需要担心释放动态分配的对象。

标签: c memory-management malloc sbrk


【解决方案1】:

有几个问题:

  1. 你在函数中间声明pointer,这在C语言中是不允许的。

  2. 您将指针设置为stack+size,但您希望它只是stack。否则,您将返回一个指向您正在分配的内存块末尾的指针。因此,如果您的调用者在该指针处使用了所有 size 字节,他将与另一块内存重叠。如果您在不同时间获得不同大小的块,您将有两个调用者尝试使用相同字节的内存。

  3. 当您执行stack += size 时,您将增加stack 不是size 字节,而是增加size void*,这几乎总是更大。

【讨论】:

  • 自 C99 起允许在函数中间声明。
  • 关于指针增量的观点不正确。当你增加一个void * 指针时,你增加的不是void * 的大小,而是void 的大小,当然。 void 没有大小,因此增量是非法的(在 C89/90 和 C99 中)。一些编译器允许将其作为扩展,在指针算术中将 void * 视为 char *(因此增量是正确的,如上所述),但无论如何它是扩展,而不是标准 C。
【解决方案2】:

除了Ned Batchelder 指出的基本问题之外,一个更微妙的问题是分配器必须返回一个与分配的任何对象正确对齐的地址。在某些平台 (x86) 上,除了性能问题之外,这可能无关紧要,但在许多平台上,这完全是一个交易破坏者。

我还必须执行(char*) 强制转换来执行stack 指针运算(您不能对void* 类型执行指针运算)。

您应该在MAX_MEMORY 宏中的表达式周围加上括号。我不认为没有它们你会遇到任何优先级问题,因为所有比乘法高优先级的运算符无论如何都不是正确的语法。使用宏,总是比抱歉更安全。 (至少有一个例外,[] 运算符只能绑定到2 而不是整个MAX_MEMORY 表达式,但看到MAX_MEMORY[arrayname] 将是一种非常奇怪的情况,即使它在语法上是有效的)。

事实上,我会把它设为枚举。

您可以通过返回一个内存块来保持分配器的简单性,该内存块针对系统上的任何基本数据类型(可能是 8 字节对齐)正确对齐:

/* Note: the following is untested                   */
/*       it includes changes suggested by Batchelder */

#include <stdio.h>
#include <unistd.h>

enum {
    kMaxMemory = 1024 * 1024 * 2, /* 2MB of memory */
    kAlignment = 8
};

void *stack = NULL; /* pointer to available stack */
void * memoryAlloc( size_t size) {
    void *pointer;

    size = (size + kAlignment - 1) & ~(kAlignment - 1);   /* round size up so allocations stay aligned */

    if (stack == NULL)
    stack = sbrk(kMaxMemory); /* give us system memory */

    pointer = stack; /* we always have space :) */
    stack = (char*) stack + size;   /* move in stack forward as space allocated */
    return pointer;
}

【讨论】:

    【解决方案3】:

    首先,正如其他人已经指出的那样,您在块的中间声明变量,这仅在 C99 中允许,但在 C89/90 中不允许。 IE。我们必须得出结论,您正在使用 C99。

    其次,您正在以 K&R 样式(无参数类型)定义您的函数,但同时稍后不声明参数类型。这样,您就依赖于 C99 中禁止的“隐式 int”规则。 IE。我们必须得出结论,您使用 C99。这已经和“第一”部分矛盾了。 (此外,习惯上使用 unsigned 类型来表示“对象大小”的概念。size_t 是通常用于该目的的专用类型。

    第三,您在 void * 指针上使用指针算术,这在 C89/90 和 C99 中始终是非法的。我什至不知道我们能从中得出什么结论:)

    请确定您要使用的语言,我们将从那里开始。

    【讨论】:

    • 我错过了size的隐式声明。
    • 感谢 AndreyT,我将使用 C89,您可以得出结论,我是一个新手,但现在有很多有用的输入 :)。
    猜你喜欢
    • 2014-10-25
    • 2010-11-07
    • 2022-01-21
    • 2010-10-16
    • 1970-01-01
    • 2010-12-21
    • 1970-01-01
    相关资源
    最近更新 更多