AdrienW
AdrienW

Reputation: 3452

Changing one given bit in a binary address in C

I'm working on the "buddy-allocation" for a memory management project in C (see page 14 of this .pdf).

I'd like to find the "buddy" of a given address, knowing that the two buddies are only one-bit-different (the size of the chunk tells us which bit changes). For example, if one of the two 32-bits buddy chunks has the binary address 0b110010100, the second one will be located at 0b110110100 (the 6th bit from the right changes, as 32=2^(6-1)).

I'd like to implement that in C, without exponentiation algorithms because I'm trying to make my program as fast-executing as possible. At best I'd use a tool to manipulate bits, if that exists. Any hints?

EDIT: the type of the addresses is void*. With the solutions posted below, gcc won't let me compile.

EDIT2: I've tried the answers posted below with the XOR operator, but I can't compile because of the type of the addresses. Here's what I've tried :

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);

and the gcc error :

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

Upvotes: 1

Views: 1138

Answers (5)

sansuiso
sansuiso

Reputation: 9379

It looks like you just want to toggle a given bit, which is achieved using an XOR operation:

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

The cast to unsigned long is required to avoid errors of undefined XOR operation on type void*.

Depending on your compiler settings, you may also get a warning about creating a pointer (i.e., an address) by casting an integer, which is obviously dangerous in the general case (you could pass an invalid address value). To silent this warning, cast back the result to void* to let the compiler know that you know what you are doing:

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

Note that in embedded system programming (where I've used this technique most of the time since many peripherals are memory-mapped) you would usually "simplify" this line of code using macros like TOGGLE_BIT(addr, bit) and INT_TO_ADDR(addr).

Upvotes: 4

unwind
unwind

Reputation: 399881

A tool? To manipulate bits? You don't need a "tool", that's about as primitive an operation as you can do.

uint32_t address = 0x0194;

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

If you really want to toggle the bit, i.e. set if if it's clear, but clear it if it's set, you use the bitwise XOR operator:

address ^= 1 << 5;

This is not "exponentiation", it's just a bitwise XOR.

If the address is held in a pointer register, either cast or copy to integer (uintptr_t) and the copy back.

Upvotes: 3

asio_guy
asio_guy

Reputation: 3767

a more compact way to do this

#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)) )

Upvotes: 1

incompetent
incompetent

Reputation: 1822

This is case of bit manipulation which is very common in c programming if you want to change xxbxxxxx simply XOR this with xx1xxxxx. XOR topple the given bit. If you want to make it 1 just use OR (|) with all bits 0 except that bit 1 which you want to turn on

Upvotes: 2

weston
weston

Reputation: 54791

You can set one bit with a | bitwise or.

adr = adr | 0x10;

Upvotes: 3

Related Questions