这是在 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 语义的同时竭尽全力打破这一点。
我编写了这段 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 语义的同时竭尽全力打破这一点。