这是在 C 中做标记指针的可移植方法吗?

Is this a portable way to do tagged pointers in C?

我编写了这段 C 代码,我认为它提供了可移植的标记指针:

typedef struct {
    char tag[2];
    int data;
} tagged_int;

#define TAG(x,y) (&(x)->tag[(y)])
#define UNTAG(x) (&(x)[-*(x)])

int main(void) {
    tagged_int myint = {{0,1}, 33};
    tagged_int *myptr = &myint;

    char *myint_tag_1 = TAG(myptr,1);
    char *myint_tag_0 = TAG(myptr,0);

    char tag_1 = *myint_tag_1;
    char tag_0 = *myint_tag_0;

    tagged_int *myint_1 = UNTAG(myint_tag_1);
    tagged_int *myint_0 = UNTAG(myint_tag_0);
}

不过,我很好奇是不是真的便携

虽然数组操作部分是可移植的,但是 char *struct * 的转换是可移植的,假设 char * 指的是 [=12= 中的第一个 field/element ]? (遗憾的是,这会输出编译器警告,但我猜你无论如何都会得到带有 "normal" 标记指针的警告...)

#define UNTAG(x) (&(x)[-*(x)]) 不作为指向 tagged_int 的指针;它是一个 char *,不会自动转换。

如果插入强制转换,#define UNTAG(x) ((tagged int *)(&(x)[-*(x)])),那么根据 C 2011(草案 N1570)6.7.2.1 15,这几乎是合法的,“指向结构对象的指针,经过适当转换,指向其初始成员(或者如果该成员是一个位域,则指向它所在的单元),反之亦然。”这里的一个障碍是你有一个指向第一个成员的第一个成员的指针(也就是说,指向 char 是第一个成员数组的元素 0)。根据您对 C 标准中某些事物的解释有多严格,这可能会或可能不会被认为是受支持的(并且可能通过使用两次转换来缓解,如 #define UNTAG(x) ((tagged int *)(char (*)[2])(&(x)[-*(x)])) 中)。无论如何,我希望它是可移植的,因为 C 实现必须在支持其他公认的 C 语义的同时竭尽全力打破这一点。