C struct继承指针对齐

thn*_*rks 48 c pointers linked-list data-structures

背景

我创建了一个主要用于学习目的的基本链表数据结构.该列表的一个目标是它可以处理不同的数据结构.因此,我已尝试在结构组合中模拟C中的"继承".以下是构成我的链表的基础的结构.

typedef struct Link {
    struct Link* next;
    struct Link* prev;
} Link;

typedef Link List;
Run Code Online (Sandbox Code Playgroud)

在我的实现中,我选择了一个作为列表头部和尾部的Sentinel节点(这就是Link == List的原因).

为了使列表实际处理数据,结构只包含Link结构作为第一个成员:

typedef struct {
    Link link;
    float data;
} Node;
Run Code Online (Sandbox Code Playgroud)

所以链表看起来像这样.

         ?????????????     ?????????     ?????????????     
... <--->? P ? N ? D ?<--->? P ? N ?<--->? P ? N ? D ?<---> ... 
         ?????????????     ?????????     ?????????????
         End Node          myList        First Node

List myList;
Node node1 = {{}, 1.024};
....
Node nodeN = {{}, 3.14};

list_init(&myList) // myList.next = &myList; myList.prev = &myList;
list_append(&myList, &node1);
....
list_append(&myList, &nodeN);
Run Code Online (Sandbox Code Playgroud)

要遍历此列表,Node指针最初指向第一个节点.然后它沿着列表遍历,直到它再次指向哨兵,然后停止.

void traverse()
{
    Node* ptr;
    for(ptr = myList.next; ptr != &myList; ptr = ptr->link.next)
    {
        printf("%f ", ptr->data);
    }
}
Run Code Online (Sandbox Code Playgroud)

我的问题是关于这条线ptr != &myList.这条线是否存在指针对齐问题?

for循环正确地产生警告:( warning: assignment from incompatible pointer typewarning: comparison of distinct pointer types lacks a cast)可以通过执行它所说的和转换来沉默Node*.但是,这是一个DumbThingToDo™吗?ptr->data当它指向&myList循环终止一次时,我永远不会访问ptr == &myList.

TLDR

在C结构中,一个Base*可以指向Derivedif Base是第一个成员Derived.可以在Derived*点到Base,如果没有的Derived特定成员进行访问?

编辑:用等效的内联代码替换相关的函数调用.

mid*_*dor 21

感谢您的演讲.

我认为您的实现应该可以正常工作,因为C保证结构的地址是其初始成员的地址.抛开C对结构成员对齐的陈述,这个保证应该意味着,只要你的实现总是将Link作为第一个成员,这不应该导致对齐问题.

这里:C99§6.7.2.1:

13在结构对象中,非位字段成员和位字段所在的单元具有按声明顺序增加的地址.指向适当转换的结构对象的指针指向其初始成员(或者如果该成员是位字段,则指向它所在的单元),反之亦然.结构对象中可能存在未命名的填充,但不是在其开头

这应该是你的意思说一下Base *Derived *,虽然在纯C.不存在这样的事情,他们只是碰巧有相同的内存布局结构.

但是我认为像这样实现它有点脆弱,因为Node和Link直接相互依赖.如果您要更改Node的结构,您的代码将变为无效.目前我没有看到额外的一点struct Link,除了你能够为重新使用链接的新类型编写新的节点.

实际上有一个链接列表实现,当我看到你的帖子并以与你打算使用你的列表的方式非常类似的方式工作时立即想到:内核列表

它使用相同的List-element(list_head):

struct list_head {
    struct list_head *next, *prev;
};
Run Code Online (Sandbox Code Playgroud)

它包含这个函数宏:

#define list_for_each_entry(pos, head, member)                          \
      for (pos = list_first_entry(head, typeof(*pos), member);        \
           &pos->member != (head);                                    \
           pos = list_next_entry(pos, member))
Run Code Online (Sandbox Code Playgroud)

如果你看一下宏的实现方式,你会发现它提供了列表条目的迭代,而不知道列表所包含的条目的布局.假设我正确地解释你的意图,我认为这是你希望它的方式.