在 C 中更改二进制地址中的一个给定位

Changing one given bit in a binary address in C

我正在为 C 语言的内存管理项目开发 "buddy-allocation"(请参阅 page 14 of this .pdf)。

我想找到给定地址的 "buddy",知道这两个伙伴只有一位不同(块的大小告诉我们哪个位发生变化)。例如,如果两个 32 位伙伴块之一的二进制地址为 0b110010100,则第二个将位于 0b1101 10100(右起第6位变化,32=2^(6-1))。

我想在 C 中实现它,而不使用求幂算法,因为我试图让我的程序尽可能快地执行。充其量我会使用一种工具来操纵位,如果存在的话。有什么提示吗?

编辑:地址类型是void*。使用下面发布的解决方案,gcc 不会让我编译。

EDIT2:我已经用 XOR 运算符尝试了下面发布的答案,但由于地址的类型我无法编译。这是我尝试过的方法:

void* ptr1 = mmap(NULL, 640000, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_FILE | MAP_PRIVATE, -1, 0);
    printf("%p\n", ptr1);
    void* ptr2 = ptr1+0x15f6d44;
    printf("%p\n", ptr2);
    void* ptr3 = (void*)(ptr2-ptr1);
    printf("%p\n", ptr3);
    void* ptr4 = ptr3 ^ (1 << 6);
    printf("%p\n", ptr4);

gcc 错误:

invalid operands to binary ^ (have ‘void *’ and ‘int’)

您可以使用 | 按位或设置一位。

adr = adr | 0x10;

工具?操纵位?您不需要 "tool",这是您所能做的最基本的操作。

uint32_t address = 0x0194;

address |= 1 << 5; /* This sets the sixth bit. */

如果你真的想切换位,即如果它是明确的则设置,但如果它已设置则清除它,你使用按位异或运算符:

address ^= 1 << 5;

不是"exponentiation",它只是按位异或。

如果地址保存在指针寄存器中,则强制转换或复制为整数 (uintptr_t) 并复制回来。

您似乎只想切换给定位,这是使用异或运算实现的:

buddy_adr = (unsigned long)adr ^ (1 << bit_location);

需要转换为 unsigned long 以避免在类型 void* 上出现未定义的 XOR 运算错误。

根据您的编译器设置,您可能还会收到有关通过强制转换整数来创建指针(即地址)的警告,这在一般情况下显然很危险(您可能会传递无效的地址值)。要消除此警告,请将结果转换回 void* 让编译器知道您知道自己在做什么:

buddy_adr = (void *)((unsigned long(adr ^ (1 << bit_location));

请注意,在嵌入式系统编程中(我大部分时间都使用这种技术,因为许多外设都是内存映射的)你通常会 "simplify" 这行代码使用像 [=15= 这样的宏] 和 INT_TO_ADDR(addr).

这是位操作的情况,在 C 编程中很常见 如果您想更改 xxbxxxxx,只需将其与 xx1xxxxx 进行异或即可。 XOR 推翻给定的位。如果你想让它成为 1,只需使用 OR (|) 和所有位 0 除了你想要打开的位 1

一种更紧凑的方式来做到这一点

#define BIT_ON(x,bit)           (x |= ( 1 << (bit-1)) )
#define BIT_TOGGLE(x,bit)       (x ^= ( 1 << (bit-1)) )
#define BIT_OFF(x,bit)          (x &= ~( 1 << (bit-1)) )