C结构inheritance指针alignment
背景
我已经创build了一个基本的链接列表数据结构主要用于学习目的。 列表的一个目标是可以处理不同的数据结构。 因此,我在结构组成方面尝试过我的手来模拟C中的“inheritance”。下面是构成我的链表的基础的结构。
typedef struct Link { struct Link* next; struct Link* prev; } Link; typedef Link List;
在我的实现中,我select了一个作为列表头部和尾部的标记节点(这就是为什么链接==列表)。
为了让列表实际处理数据,一个结构只包含Link结构作为第一个成员:
typedef struct { Link link; float data; } Node;
所以链表就像这样
┌───┬───┬───┐ ┌───┬───┐ ┌───┬───┬───┐ ... <--->│ 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);
题
要遍历这个列表, Node
指针最初指向第一个节点 。 然后它沿着列表遍历,直到它再次指向标记,然后停止。
void traverse() { Node* ptr; for(ptr = myList.next; ptr != &myList; ptr = ptr->link.next) { printf("%f ", ptr->data); } }
我的问题是与行ptr != &myList
。 这行有没有指针alignment的问题?
for循环正确地产生警告:( warning: assignment from incompatible pointer type
, warning: comparison of distinct pointer types lacks a cast
强制转换),可以通过执行它的内容并转换为Node*
来使其静音。 不过,这是一个DumbThingToDo™吗? 当它指向&myList
时,我从来没有访问过ptr->data
,因为循环终止了一次ptr == &myList
。
TLDR
在C结构中,如果Base
是Derived
中的第一个成员,则Base*
可以指向Derived
。 如果没有Derived
特定的成员被访问, Derived*
指向Base
吗?
编辑 :用相应的内联代码replace相关的函数调用。
荣誉您的演示文稿。
我认为你的实现应该工作正常,因为C保证结构的地址是其初始成员的地址。 抛开C关于struct-membersalignment的陈述,这个保证意味着,只要你的实现总是把link作为第一个成员,这不应该引起alignment问题。
从这里开始 :C99§6.7.2.1:
13在一个结构体对象中,非位域成员和位域所在的单元的地址增加了它们的声明顺序。 指向结构对象的指针(适当地转换)指向其初始成员(或者如果该成员是位域,则指向其驻留的单元),反之亦然。 结构对象中可能有未命名的填充,但不在其开头
这应该是你要说的Base *
和Derived *
,虽然在纯C中不存在这样的事情。它们只是恰好具有相同内存布局的结构。
不过我觉得像这样实现它有点脆,因为Node和Link直接相互依赖。 如果你要改变Node的结构,你的代码就会失效。 目前我没有看到有一个额外的struct Link
,除了你能够为一个新的types重新使用链接写一个新的节点。
实际上有一个链表实现,当我看到你的文章并且以与你打算使用你的列表的方式非常相似的方式工作时,立即想到了这个: 内核列表
它使用相同的List元素(list_head):
struct list_head { struct list_head *next, *prev; };
它包含这个函数macros:
#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))
如果你看看这个macros是如何实现的,你会发现它提供了对列表条目的迭代,而不知道列表包含的条目的布局。假设我解释你的意图是正确的,我认为这是你会希望它的方式。
在C结构中,如果Base是Derived中的第一个成员,则Base *可以指向Derived。 如果没有Derived特定的成员被访问,派生*可以指向Base吗?
如果您的意思是“可以派生*指向任意基础(包括不是派生对象的第一个成员)”,那么:从技术上讲,不。 我对缺陷报告#74的理解是alignment要求可以不同。
问:如果一个结构有一个ttypes的字段,字段的alignment要求是否可以不同于同types的不是结构成员的对象的alignment要求? 如果对(a)的答案是“是”,那么在适用的情况下,应该假定其余的问题已经被要求在结构外的结构和对象内的两个对象。
答:6.1.2.5节说:'…指向兼容types的合格或不合格版本的指针应具有相同的表示和alignment要求。 子条款6.5.2.1说:“结构或联合对象的每个非位域成员都按照实现定义的方式与其types相匹配。 然后,“因此,在结构对象内可能有一个未命名的填充,如果需要,可以实现适当的alignment。 a)一个实现有可能陈述一般化的需求来满足子目录6.1.2.5。 这些要求可以使用6.5.2.1中提供的实现定义的行为来进一步加强。 是的,alignment要求可能不同。
我在给ouah的回答的评论中写了这个,但是我将以它自己的权利来回答。
ouah写道,如果按C标准行事,那么你的问题中的代码在理论上可能会有一个alignment问题。 然而,我不认为你的代码有可能(或不太可能)运行在ABI展示这种alignment模式的单一架构上。 至less没有我知道的,当然也包括几个不同的桌面和embedded式体系结构的经验。 我也无法真正想象一个能从这种alignment属性中获得任何东西的架构。
值得注意的是,这种模式在实践中确实是相当常用的; 这有点类似于你可以随心所欲地指出这一点,因为它不受任何跨平台标准的保证,但实际上没有任何平台是不正确的,而且人们总是这样做。