【问题标题】:Setting least significant bit of a pointer to 0 [duplicate]将指针的最低有效位设置为 0 [重复]
【发布时间】:2015-05-09 00:13:33
【问题描述】:

我有一个 size_t * 变量,我想将其最低有效位设置为 0。由于它从 1 开始,我尝试了以下方法。

size_t * next_chunk = stack_mem.start;
int n = (int)next_chunk;
n ^= 1 << 1;
next_chunk = (size_t)n;

这可能是大错特错。

【问题讨论】:

  • 首先,next_chunk 是一个您似乎使用不正确的指针。你可能需要做int n = (int)*next_chunk;
  • 您为什么要这样做?以这种方式操纵指针值是一件可怕的事情。然后,永远不要通过int 投射指针,这可能会丢失信息。最好的类型是uintptr_t,它保证有必要的宽度,并且因为它是一个无符号类型,所以通常的位操作是明确定义的。
  • size_t*int 的转换是非常不安全的,因为指针的大小通常是 4 或 8 字节,而整数的大小通常是 2 或 4 字节(取决于您的平台)。顺便说一句,更糟糕的是,“在回来的路上”,你甚至没有回滚到size_t*,而是回溯到size_t!!!
  • @ForceBru:我相信 OP 试图掩盖指针的值,而不是指向数据的值(否则,他为什么要转换为 int 而不是size_t?)。

标签: c pointers bit-manipulation


【解决方案1】:

首先,您必须确保使用与指针大小相同的整数类型。在大多数 64 位平台上,ints 是 32 位的,而指针是 64 位的,因此在转换为 int 时会损坏指针。 size_t 通常可以完成这项工作,除了一些奇异的内存模型。

然后我建议使用一个联合,它允许在不进行任何强制转换的情况下修改指针的位:

union {
    size_t *pointer;
    size_t  integer;
} u;

u.pointer = next_chunk;
u.integer &= ~1;
next_chunk = u.pointer;

正如其他人已经指出的那样,您可以通过与按位取反的位模式进行与运算来清除整数的位,在最低有效位的情况下为 1。

【讨论】:

  • 为什么你更喜欢工会而不是演员?联合意味着对未修改的数据进行就地重新检查(因此对浮点位模式等有用),但在这里没有理由这样做:在精心设计的代码中,“读取模式”与“检查模式”,因此让系统转换值(在任何安全的地方都可能是无操作)而不对指针表示做进一步的假设是语义上更干净的事情。 (当这正是intptr_t 存在的目的时,为什么还要使用size_t?)
  • @Leushenko 任何体面的编译器都会在通过联合转换时生成最佳代码。所以最后,这只是一个品味问题。我选择了size_t,因为intptr_t 仅在C99 中可用。
  • 通用系统上的代码应该是一样的,但是联合意味着不同的访问模式。这主要是一个设计问题,尽管系统在转换指针和整数 (Keith Thompson gives an example) 时可以使用除 no-op 之外的其他东西,这会使联合在稀有平台上无效。请注意,共享联合字段也是 C99 功能。
  • @Leushenko 我不明白为什么联合会暗示某种访问模式。您是对的,通过联合访问可能具有与强制转换不同的语义,但这不会使操作无效。此外,Keith Thompson 提到的 Cray T90 在将指针转换为整数时似乎仍然使用空操作。只有指针的按位表示是不寻常的。 “共享联合字段”是什么意思?我的代码是有效的 C89。
  • 好的,它不会是无效的,但它会有所不同。也许这不是问题。在联合上,C89 将其声明为“实现定义的”并且不保证位模式实际上会被保留,而 C99/11 会(尽管我将其误记为“未定义”,这是完全不同的蠕虫罐头) )。
【解决方案2】:

将从位位置 1 开始的最低有效位设置为值 0 的工作原理如下。

n &= ~((0x01) << 1)

我认为有问题的 sn-p 中的代码没有正确使用。

【讨论】:

    【解决方案3】:

    试试下面的

    n &= ~0 << 1;
    

    另一种方法是

    n = ( ( unsigned int )n >> 1 ) << 1;
    

    考虑到将指针转换为 int 类型的对象是不安全的。

    如果您的意思是设置指针指向的对象的最低有效位,那么操作将如下所示

    *next_chunk &= ~0 << 1
    *next_chunk = ( *next_chunk >> 1 ) << 1;
    

    【讨论】:

    • 这可能会解决手头的特定“位问题”,但您并不是指 OP 代码中的其他几个(主要)问题。从size_t*int 的转换非常不安全,因为指针的大小通常为4 或8 个字节,而整数的大小通常为2 或4 个字节(取决于您的平台)。顺便说一句,更糟糕的是,“在回来的路上”,OP 甚至没有投射到 size_t* 而是投射到 size_t !!!
    • 从指针转换为 int 并返回是安全的。使用 right int 类型(即uintptr_t)不会有任何问题。
    【解决方案4】:

    您确定要取消设置内存中某个位置的最低有效位吗?我想,您想使用位于具有此地址的位置的值来执行此操作。也许,你需要

    size_t * next_chunk = stack_mem.start;
    *next_chunk &= ~0 << 1;
    

    UPD:在所有概率下,指针值(即内存地址)都会对齐,换句话说,它的最后一位或两位为零。

    UPD2:要自己进行某种对齐,您需要删除第二行开头的星号

    size_t * next_chunk = stack_mem.start;
    next_chunk &= ~0 << 1;
    

    【讨论】:

    • 我相信 OP 试图掩盖指针值中的一点,而不是指向数据的值(否则,他为什么要转换为 int 而不是 size_t ?)。
    • @barakmanos 我不能确定。这就是我问的原因。
    • 我正在尝试编辑实际指针而不是它指向的值
    • 另外,如果您想知道最低有效位通常会告诉您数据是空闲的还是在 C 中分配的,我正在尝试制作一个垃圾收集器
    • @ManPerson 我为我的误解道歉。
    猜你喜欢
    • 2014-04-07
    • 2010-10-19
    • 2013-11-13
    • 2016-06-16
    • 2017-03-20
    • 2016-10-14
    • 2018-02-05
    • 1970-01-01
    • 2019-12-22
    相关资源
    最近更新 更多