【问题标题】:Is this a portable way to do tagged pointers in C?这是在 C 中进行标记指针的可移植方式吗?
【发布时间】:2018-07-01 17:02:11
【问题描述】:

我已经编写了这个 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 * 指的是 struct * 中的第一个字段/元素? (这很遗憾地输出编译器警告,但我猜你无论如何都会得到带有“正常”标记指针的那些......)

【问题讨论】:

  • 不确定您要在这里做什么。标签打算提供什么?
  • @SteveSummit 这是一个标记指针。它提供了一些数据作为指针的一部分,而不是结构的一部分。
  • 对我来说看起来是可移植的,但我没有看到任何用例,((&(x)[-*(x)]) 是不可读的,例如 ((x) - *(x)) 看起来更好,我们看到这段代码现在看起来很奇怪)
  • 我不清楚你的实现,但我没有看到指针中存储了任何标签。 Tagged pointer 表示指针本身的某些位用于标记。您只有结构中的数据和标记,然后指向结构。即使您将指针存储在data 字段中,显然它也不是可移植的,因为指针不是int。它们只能转换为(u)intptr_t
  • @SoniEx2 好的,但我真的没有看到您将任何额外数据存储为指针的一部分。 (这是一种流行的技术,但我相信它总是不可移植,因为它完全依赖于机器和系统,指针中是否有任何位保证不会被使用,这样你就可以“窃取”它们供您自己使用而不会产生不良影响。)

标签: c pointers language-lawyer portability


【解决方案1】:

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

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

【讨论】:

  • 我的想法是TAGUNTAG 应该适用于任何可标记的结构,所以我猜你会在使用UNTAG 时添加演员表(而不是UNTAG 本身).. .(tagged int *)(char (*)[2])UNTAG(x)我猜?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-12-03
  • 1970-01-01
  • 2013-12-17
  • 1970-01-01
  • 1970-01-01
  • 2021-10-22
  • 2015-03-09
相关资源
最近更新 更多