You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过置最后一位为1标记指针?位操作解引用遇段错误

位操作标记指针后解引用触发段错误的原因分析

为实现非阻塞链表,我尝试利用指针未使用的最后一位做标记——用OR操作置最后一位为1,AND操作复位该位,AND操作检查标记位。但使用位操作宏(如#define unmark(x) (x & (uintptr_t) 0xfffffffe))时,即便unmark后指针的整数值看起来正确,解引用仍会触发段错误;而用x-1的取消标记宏却能正常运行。已知加减1的方案存在架构相关性(当前架构指针末位始终为0),且指针操作本身可移植性较差,但想明确位操作导致段错误的具体原因。测试代码如下:

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>

//This produces segfault, for some reason
//#define unmark(x)     (x & (uintptr_t) 0xfffffffe)
//#define mark(x)   (x | (uintptr_t) 0x00000001)
#define is_marked(x)    ((long) x & 0x00000001)

#define mark(x)     x + 1
#define unmark(x)   x - 1

struct Example {
     long x;
     long y;
};

int main() {
    struct Example *x = malloc(sizeof(struct Example));
    x->x = 10;
    x->y = 20;
    uintptr_t p = (uintptr_t)(void*) x;

    printf("%ld\n", ((struct Example *) (void*) p)->y);

    printf("%04x\n", p);
    printf("Is marked: %d\n", is_marked(p));

    p = mark(p);
    printf("%04x\n", p);
    printf("Is marked: %d\n", is_marked(p));

    p = unmark(p);
    printf("%04x\n", p);
    printf("Is marked: %d\n", is_marked(p));

    printf("%ld\n", ((struct Example *) (void*) p)->y);

    return 0;
}

核心问题:固定长度掩码导致高地址位丢失

你遇到的段错误,根源出在位掩码的长度不匹配上:

  • 你使用的0xfffffffe是一个32位的十六进制常量,当强制转换为uintptr_t时,如果你的系统是64位架构(大部分现代系统都是),这个掩码只会覆盖低32位,高32位会被填充为0,变成0x00000000fffffffe。
  • 执行unmark(x)时,这个掩码会把指针的高32位全部清零,导致指针指向完全无效的内存地址,解引用自然触发段错误。
  • 而加减1的操作是对整个uintptr_t数值进行的,只会修改最后一位,不会破坏高地址位,因此能正确还原原指针。

额外问题:类型转换的潜在风险

你的is_marked宏使用(long)x进行转换,这在64位系统下可能隐含符号扩展问题:如果指针的最高位为1,转换为有符号的long会变成负数,但判断最后一位的逻辑暂时不受影响。更稳妥的做法是用uintptr_t来转换,避免符号干扰:

#define is_marked(x)  ((uintptr_t)x & 1)

修复方案:使用与架构无关的掩码

要让位操作宏在32位和64位系统下都正常工作,需要生成与指针位数匹配的掩码,推荐两种写法:

  1. 取反单比特掩码:~(uintptr_t)1,会生成所有位为1、仅最后一位为0的掩码,适配任何指针宽度。
  2. 利用补码特性:(uintptr_t)-2,-2的无符号补码形式同样是仅最后一位为0的全1掩码。

修复后的位操作宏如下:

#define unmark(x)     (x & ~(uintptr_t)1)
#define mark(x)       (x | (uintptr_t)1)
#define is_marked(x)  ((uintptr_t)x & 1)

替换后,unmark操作会正确保留所有高地址位,还原出原始指针,解引用就不会触发段错误了。

内容的提问来源于stack exchange,提问作者Andrejcc

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 09:05:32