将struct指针转换为另一个嵌入式结构

时间:2012-07-10 18:44:13

标签: c pointers struct stack

我正在实现一个不需要内存分配的堆栈,只要它嵌入一个特殊的结构就可以使用任何结构。与GNU's List实施类似。

任何结构必须嵌入在堆栈中工作的结构是:

struct stack_elem {
  struct stack_elem *next;
};

我希望与堆栈一起使用的结构是:

struct node {
  double number;
  char character;
  int isNumber;
  struct stack_elem elem;
};

所以堆栈看起来像这样:

node:            node:
+---------+      +---------+
|number   |      |number   |
+---------+      +---------+
|character|      |character|
+---------+      +---------+
|isNumber |      |isNumber |
+---------+      +---------+
|elem     |      |elem     |
| *next   |----->| *next   |     
+---------+      +---------+ etc....

我正在编写一个名为stack_entry的宏来将嵌入的elem结构转换为其容器node结构。这是我到目前为止所尝试的内容。 stack_entry将按如下方式使用:

struct stack_elem *top = peek(&stack);
struct node *converted_node = stack_entry(top, struct node, elem);

stack_entry将指向stack_elem,要转换为的节点的类型以及该节点中元素的成员字段的名称。我尝试了几种方法来做但没有工作。我意识到STACK_ELEM指向next项,因此我尝试从elem中减去struct node的偏移量,但这不起作用。以下是我尝试过的一些不起作用的事情:

#define stack_entry(STACK_ELEM, STRUCT, MEMBER)       \
         ((STRUCT *) &(STACK_ELEM) - offsetof (STRUCT, MEMBER))

#define stack_entry(STACK_ELEM, STRUCT, MEMBER)       \
         ((STRUCT *) ((uint8_t *) &(STACK_ELEM)->next \
         - offsetof (STRUCT, MEMBER)))

什么是正确的算术?为什么不减去next的偏移量,因此elem的偏移量,如果它是结构中的最后一个元素,则会产生节点?

GNU列表list_entry宏定义为:

#define list_entry(LIST_ELEM, STRUCT, MEMBER)           \
        ((STRUCT *) ((uint8_t *) &(LIST_ELEM)->next     \
                     - offsetof (STRUCT, MEMBER.next)))

这是如何工作的?为什么next在它应该在同一个节点内而不是next中抓取容器结构时参与其中?

2 个答案:

答案 0 :(得分:3)

GNU的list_entry通过从next字段的地址减去从结构开头到next字段的偏移量来工作。它实际上并没有取消引用next指针。在名为container_of的Linux源代码中有一个常用的宏,您可能会发现它很有帮助。链接的文章非常有用。

在第二次尝试定义stack_entry时,减去错误的偏移量(将其与GNU list_entry宏进行比较)。

答案 1 :(得分:1)

您的实现实际上不是堆栈,而是单个链接列表或向量,具体取决于您的数据结构是否具有任意大小。

仅供您考虑:

  • 如果您的元素大小相同,为什么还需要下一个指针。它们不应该相互抵消吗? 假设您的结构大小为16个字节。第一个元素位于地址 base ,下一个元素位于地址 base + 16 n -th元素位于 base +(n-1) * 16 (然后你称之为向量)

  • 如果您的数据结构具有任意大小,则需要两件事:找出它是哪个对象以及固定偏移到下一个指针的位置。两个好的提示:

对您来说最好的事情是:它已由其他人实施:您可以使用#include <sys/queue.h> SLIST,然后您只需要处理数据的放置位置。