当前位置: 首页 > 知识库问答 >
问题:

便携式标记指针

羊昊苍
2023-03-14

是否有一种可移植的方法来实现C/C中的标记指针,比如一些跨平台和编译器工作的有文档记录的宏?或者,当你标记你的指针时,你就有危险了?如果存在这样的助手函数/宏,它们是任何标准的一部分还是仅作为开源库提供?

对于那些不知道标记指针是什么但感兴趣的人来说,这是一种在普通指针中存储一些额外数据的方法,因为在大多数体系结构中,指针中的某些位总是0或1,所以您将标记/类型/提示保留在这些额外位中,在你们想用指针去引用某个实际值之前,把它们擦掉。

const int gc_flag = 1;
const int flag_mask = 7; // aka 0b00000000000111, because on some theoretical CPU under some arbitrary OS compiled with some random compiler and using some particular malloc last three bits are always zero in pointers.

struct value {
   void *data;
};

struct value val;
val.data = &data | gc_flag;
int data = *(int*)(val.data & flag_mask);

https://en.wikipedia.org/wiki/Pointer_tagging

共有3个答案

江子石
2023-03-14

嗯,GCC至少可以计算位字段的大小,所以你可以跨平台移植(我没有MSVC可以测试)。您可以使用它将指针和标记打包成intptr_t,并且intptr_t保证能够容纳指针。

#include <limits.h>
#include <stdio.h>
#include <stdint.h>
#include <stddef.h>
#include <inttypes.h>

struct tagged_ptr
{
  intptr_t ptr : (sizeof(intptr_t)*CHAR_BIT-3);
  intptr_t tag : 3;
};

int main(int argc, char *argv[])
{
  struct tagged_ptr p;

  p.tag = 3;
  p.ptr = (intptr_t)argv[0];

  printf("sizeof(p):              %zu <---WTF MinGW!\n", sizeof p);
  printf("sizeof(p):              %lu\n", (unsigned long int)sizeof p);
  printf("sizeof(void *):         %u\n", (unsigned int)sizeof (void *));
  printf("argv[0]:                %p\n", argv[0]);
  printf("p.tag:                  %" PRIxPTR "\n", p.tag);
  printf("p.ptr:                  %" PRIxPTR "\n", p.ptr);
  printf("(void *)*(intptr_t*)&p: %p\n", (void *)*(intptr_t *)&p);
}

给予:

$ ./tag.exe
sizeof(p):              zu <---WTF MinGW!
sizeof(p):              8
sizeof(void *):         8
argv[0]:                00000000007613B0
p.tag:                  3
p.ptr:                  7613b0
(void *)*(intptr_t*)&p: 60000000007613B0

我已将标记放在顶部,但更改结构的顺序会将其放在底部。然后将要存储的指针向右移动3将实现OP的用例。可能会制作宏以便于访问。

我也有点喜欢这个结构,因为你不能像普通指针一样意外地取消引用它。

裴金鑫
2023-03-14

如果您确定您传递的地址总是有某些未使用的位,那么您可以使用uintptr_t作为传输类型。这是一个整数类型,它以预期的方式映射到指针(并且在不提供这种可能映射的模糊平台上不存在)。

虽然没有任何标准宏,但您可以很容易地使用自己的宏。代码(SAN宏)可能如下所示:

void T_func(uintptr_t t)
{
    uint8_t tag = (t & 7);
    T *ptr = (T *)(t & ~(uintptr_t)7);

    // ...
}

int main()
{
    T *ptr = new T;
    assert( ((uintptr_t)ptr % 8) == 0 );
    T_func( (uintptr_t)ptr + 3 );
}

这可能会破坏涉及跟踪指针使用情况的编译器优化。

蓬英逸
2023-03-14

通过保证对象与1的倍数对齐,您可以获得供个人使用的地址的最低N位

struct Data { ... };

alignas(1 << 4) Data d; // 4-bits, 16-byte alignment
assert(reinterpret_cast<std::uintptr_t>(&d) % 16 == 0);

// dynamic (preferably with a unique_ptr or alike)
void* ptr = std::aligned_alloc(1 << 4, sizeof(Data));
auto obj = new (ptr) Data;
...
obj->~Data();
std::free(ptr);

你的代价是丢弃大量内存,随着所需的比特数呈指数级增长。此外,如果您计划连续分配许多这样的对象,这样的数组将不适合相对较小的数组的处理器cacheline,可能会大大减慢程序。因此,这种解决方案是不按比例的。

 类似资料:
  • 我正在运行一个示例管道,我的环境就是这样。 Python"SaiResearch-Apache-Beam-Spark.py"--run=PortableRunner--job_endpoint=192.168.99.102:8099 我的Spark运行在Docker容器上,我可以看到JobService运行在8099。 我得到以下错误:grpc_频道_多线程登录: 当我卷曲到ip: port时,我

  • 可移植性是任何编程语言的重要方面。 众所周知,Rexx可用于各种操作系统,如Windows和Linux。 因此,需要确保在Windows平台上开发程序时,如果在Linux平台上运行相同的程序,则需要采取必要的预防措施。 Rexx能够运行系统级命令。 有些命令可用于了解运行它的操作系统是什么。 根据输出,它可以采取适当的操作来查看可以在此操作系统上运行的命令。 例子 (Example) 以下示例显示

  • 也许有人可以帮助Windows、Python、Selenium和使用Chrome webdriver与ChromePortable。 我定义了一个新文件夹 你知道吗?提前谢谢你,祝你一天愉快安德烈亚斯

  • 本文向大家介绍sed便携使用,包括了sed便携使用的使用技巧和注意事项,需要的朋友参考一下 示例 就地编辑虽然很常见,但却是一种非标准功能。一个可行的替代方法是使用中间文件来存储原始文件或输出。 要将-i选项与GNU和FreeBSD语法一起使用,必须指定扩展名并将其附加到-i选项中。两者都将接受以下内容,并产生两个文件:的原始版本file.orig和的编辑版本file: 请参阅给定文件的基本示例f

  • 我正在尝试使用java applet访问windows 7中显示为便携式设备的设备中的一些文件(其中包含“windows CE”应用程序)。。。。 我的设备路径是 同样我用了 当我导航到已连接的设备并右键单击文件并查看其属性时,它会将其位置显示为 此外,当我打开此文件时,它会自动放在我电脑的临时文件中。我也在使用签名小程序,所以不存在文件访问被拒绝的问题 我也用过

  • 问题内容: 是否可以运行comserver而无需提升权限。 例如,我能够从Python.TestServer(如下)运行代码,但它需要提升。 Python.TestServer代码位于:从.NET消耗Python COM Server 是否可以运行不需要提升的com服务器,以便我可以在没有管理密码的情况下运行com对象。 例如 我已经尝试了上面的代码,但它说 如何使本地服务器的有效类字符串? 示例