为列表节点分配内存的安全方法

时间:2009-04-03 07:48:35

标签: c memory-management list portability

在一个简单的清单中,例如:

struct Node {  
    Node *next;  
    void *data;  
}  

如果我在单个分配中分配节点和数据(如果我知道大小),有什么问题,比如

Node * t = (Node*)malloc(sizeof(Node) + DataSize));  

并始终在分配的块的末尾分配数据,

t->data = (BYTE*)t+ sizeof(Node); /* BYTE is byte length, can use char in gcc */

节点和数据将在sinlge go中被删除,因此没有真正的问题紧密耦合它们(按设计)

我正在研究可移植性问题(特别是包装)或其他未知问题?

这种分配方式安全且便携吗?

4 个答案:

答案 0 :(得分:4)

正如Dirkgently所说,不要假设C中malloc()的返回,这样做是没用的,可以隐藏错误。

另外,为了计算Node标头后面的地址,我发现这样做更清晰:

t->data = t + 1;

这是有效的,因为t是一个类型化的指针,所以算术运算正常。添加一个以指向数据的大小递增它,即在这种情况下为sizeof (Node)。我发现这个用法惯用于这种特殊情况,即紧接着malloc() ed之后​​的地址计算地址(当“某些东西”是具有静态已知大小的明确定义的类型时,作为Node结构在这种情况下,当然)。

这有以下好处:

  • 不重复类型名称。
  • 没有sizeof,所以更短。
  • 再次,没有演员。
  • 非常简单的算术,易于阅读。

我意识到在正确声明之前使用Node类型时出现错误。我不同意dirkgently的解决方案,这是它的外观,在C:

/* This introduces the type name "Node", as an alias for an undefined struct. */
typedef struct Node Node;

struct Node {
  Node *next; /* This is OK, the compiler only needs to know size of pointer. */
  void *data;
};

为了完整性,因为我从不厌倦地展示我应该如何编写这样的代码,这里是一个创建一个新节点来保存n个字节数据的函数示例:

Node * node_new(size_t n)
{
  Node *node;

  if((node = malloc(sizeof *node + n)) != NULL)
  {
    node->next = NULL;
    node->data = node + 1;
  }
  return node;
}

就是这样。请注意在sizeof调用中对指针目标使用malloc(),以避免重复类型名称并在类型应该更改时产生易于忘记的依赖关系。

答案 1 :(得分:3)

从技术上讲,如果数据具有对齐要求,则可能不安全。 malloc()返回一个适合所有类型的指针,t+1也可能是对齐的。可能,但不能保证。

答案 2 :(得分:2)

在创建别名之前,不能将Node用作类型。使用:

typedef struct Node {
   struct Node* n;
   void* data;
}Node;

如果您要坚持使用C编译器,则无需转换malloc的结果。我发现以下内容更易于维护和阅读:

Node* t = malloc(sizeof *t + DataSize);

BYTE不是语言定义的标准类型,因此不可移植。以下几行试图完成什么?

t->data = (BYTE*)t+ sizeof(Node); 

如果要分配某些内容,请填写以下内容:

t->data = pointer to some data ...

如果要获取字节偏移量,请使用offsetof宏。

包装是特定于实施的。您必须参考相应的编译器文档并查看可用的内容。

此外,您可能希望拥有一个head对象来维护有关列表的管理信息(长度等)。

答案 3 :(得分:1)

我同意@MSalters。只要你有一个间接级别(data指针),你也可以做正确的事情并为它分配一个单独的块。

现在,另一种方法是使用C99中定义的灵活数组成员

typedef struct Node {
    struct Node *next;
    char data[];
} Node;

Node *n = malloc(sizeof(*n) + DataSize * sizeof(*data));
//if (n != NULL), access data[0 ~ DataSize-1]

sizeof(struct Node)补充了适当数量的填充(如果有),以满足data的对齐要求,并且data[]的行为就像它被声明为数组一样适合malloc()返回的内存块的最大可能大小。这适用于所有类型的data,而不仅仅是char(因此第二个sizeof)。