我正在为一种语言设计一个抽象语法树(AST)。我的词法符号看起来像
typedef struct {
tokenHeader header;
tokenData data;
} lexicalToken;
,其中tokenHeader
和tokenData
为struct
s。对于AST节点,我有一个基本类型:
#define AST_HEADER
struct astNode *parent;
tokenHeader header;
typedef struct astNode {
AST_HEADER
} astNode;
以及一些分割类型。例如,
typedef struct {
AST_HEADER
astNode *nodes[2];
} astTwoSplitNode;
我没有在AST_HEADER
中包含tokenData
的原因是因为几乎所有令牌都不使用该字段。这让我想到
typedef struct {
astNode *parent;
lexicalToken token;
// Nodes which use tokenData are always terminal nodes.
} astNodeWithData;
这最后给我带来了我的问题:astNodeWithData
是否保证以这样一种方式安排,我可以将astNodeWithData*
转换为astNode*
,引用header
字段,并以预期的方式访问其字段?
如果没有,是否有办法指示我的编译器(gcc,如果它有帮助)强制执行这样的安排?
实际上可以将astNodeWithData *
类型强制转换为astNode **
类型,而不是astNode *
类型,因为结构体的地址与第一个成员的地址相同。
C标准第6.7.2.1p15节关于"结构和联合说明符";州:
在结构对象中,非位域成员和位域所在的单元的地址增加它们被声明的顺序。指向结构体的指针对象经过适当转换后,指向其初始成员(或)如果该成员是位域,则指向其所在的单元类中可能存在未命名的填充结构对象,但不在其开头
因此以下语句是允许的:
astNodeWithData dataNode;
// populate dataNode
astNode **nodePtr = (astNode **)&dataNode;
// access (*nodePtr)->header
astNodeWithData *dataNodePtr = (astNodeWithData *)nodePtr;
// access dataNodePtr->token