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

在一个简单的列表中,ex:

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中删除,因此没有真正的问题紧密耦合它们(按设计)

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

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

正如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; } 

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

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

在创建别名之前,不能将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对象来维护有关列表的一些内务信息(长度等)。

我同意@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 )。