C语言结构体字段对齐问题:astNodeWithData转astNode*是否合法
问题结论
你当前定义的astNodeWithData不能得到C标准的内存布局保证,直接强制转换为astNode*访问header字段属于未定义行为。
原因分析
C标准对结构体公共初始序列的合规要求是:两个结构体的起始成员的类型、顺序、对齐属性完全一致,且两个结构体属于同一个union的成员时,才允许跨类型访问公共起始成员。
你当前的两个结构体的起始成员序列并不匹配:
astNode展开后的成员顺序是:astNode* parent->tokenHeader headerastNodeWithData的成员顺序是:astNode* parent->lexicalToken tokenlexicalToken的第一个成员虽然是tokenHeader,但C标准不保证lexicalToken类型的起始偏移和tokenHeader类型的起始偏移完全对齐(哪怕实际测试在大部分场景下偏移一致,也不属于标准保证的范围),因此直接通过转换后的astNode*访问header字段的行为是不可靠的。
修复方案
方案1:调整结构体定义,符合标准公共初始序列要求(最推荐)
直接将AST_HEADER宏放到astNodeWithData的起始位置,保证和astNode的起始成员完全匹配:
typedef struct { AST_HEADER // 直接复用公共头,保证和astNode的parent、header顺序、类型完全一致 tokenData data; // 紧跟header存放tokenData,和lexicalToken的内存布局完全兼容 } astNodeWithData;
如果需要用到完整的lexicalToken类型,也可以用union实现,不影响内存布局:
typedef struct { AST_HEADER union { tokenData data; lexicalToken full_token; }; } astNodeWithData;
这种写法是完全符合C标准的,任意C编译器都会保证你可以将astNodeWithData*安全转换为astNode*,正常访问parent和header成员。
方案2:强制gcc使用无填充布局(不推荐)
如果你坚持使用当前的astNodeWithData定义,可以给相关结构体加上gcc特有的打包属性,强制取消结构体内部的填充对齐:
typedef struct __attribute__((packed)) { tokenHeader header; tokenData data; } lexicalToken; typedef struct __attribute__((packed)) { astNode *parent; tokenHeader header; } astNode; typedef struct __attribute__((packed)) { astNode *parent; lexicalToken token; } astNodeWithData;
该方案可以让gcc保证astNode->header和astNodeWithData->token.header的偏移一致,但会带来成员访问性能下降的问题,部分非x86架构下还可能触发未对齐访问崩溃,仅作为兜底方案使用。
内容的提问来源于stack exchange,提问作者Daniel Walker
相关产品推荐
相关产品推荐

