1

I have a size_t * variable of which I want to set the least significant bit to 0. Since it starts at 1, I tried the following approach.

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

This is probably horribly wrong.

nwellnhof
  • 32,319
  • 7
  • 89
  • 113
Man Person
  • 1,122
  • 8
  • 20
  • 34
  • 1
    First of all, `next_chunk` is a pointer which you seem to be using incorrectly. You may need to do `int n = (int)*next_chunk;` – ForceBru Mar 07 '15 at 12:33
  • Why would you want to do this? Manipulation of pointer values in that way is a horrible thing to teach. Then, never cast pointers through `int`, this may loose information. The best type for this is `uintptr_t`, it is guaranteed to have the necessary width and since it is an unsigned type usual bit operations are well defined. – Jens Gustedt Mar 07 '15 at 12:34
  • That casting from `size_t*` to `int` and back is extremely unsafe, since the size of a pointer is typically 4 or 8 bytes, and the size of an integer is typically 2 or 4 bytes (depending on your platform). By the way, to make things worse, "on the way back" you are not even casting back to `size_t*` but to `size_t`!!! – barak manos Mar 07 '15 at 12:40
  • 1
    @ForceBru: I believe that OP is trying to mask a bit in the value of the pointer, not in the value of the pointed data (otherwise, why would he cast to `int` and not to `size_t`?). – barak manos Mar 07 '15 at 12:45
  • http://stackoverflow.com/questions/11815894/how-to-read-write-arbitrary-bits-in-c-c – dtech Mar 07 '15 at 12:50

4 Answers4

3

First you have to make sure to use an integer type that has the same size as a pointer. On most 64-bit platforms, ints are 32-bit and pointers are 64-bit, so you'll corrupt the pointer when casting to an int. size_t usually does the job, except for some exotic memory models.

Then I'd recommend to use a union which allows to modify the bits of a pointer without any casts:

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

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

As others have already noted, you can clear bits of an integer by ANDing with the bitwise negated bit pattern which is 1 in case of the least significand bit.

nwellnhof
  • 32,319
  • 7
  • 89
  • 113
  • Why would you prefer a union over a cast for this? A union implies an in-place re-examination of the data unmodified (hence being useful for e.g. float bit patterns), but there's no reason to do that here: in well-designed code there should never be any overlap between the value in "read mode" vs "inspect mode", so letting the system convert the value (which will probably be a no-op anywhere this is safe to do) without making yet further assumptions about pointer representation is the semantically cleaner thing. (And why use `size_t` when this is exactly what `intptr_t` exists for?) – Alex Celeste Mar 07 '15 at 14:03
  • @Leushenko Any decent compiler will generate optimal code when converting through unions. So in the end, it's just a matter of taste. I chose `size_t` because `intptr_t` is only available in C99. – nwellnhof Mar 07 '15 at 14:38
  • The code should be the same on common systems, but the union implies a different access pattern. It's mostly a design issue, although a system *can* use something other than a no-op when converting pointers and integers ([Keith Thompson gives an example](http://stackoverflow.com/a/18578480/1366431)), which would make the union invalid on rare platforms. Note that sharing union fields is a C99 feature too. – Alex Celeste Mar 07 '15 at 14:47
  • @Leushenko I don't see why a union would imply a certain access pattern. You're right that access through a union could have different semantics than casting, but this wouldn't make the operation invalid. Also, the Cray T90 mentioned by Keith Thompson still seems to use a no-op when casting pointers to integers. Only the bitwise representation of pointers is unusual. What do you mean by "sharing union fields"? My code is valid C89. – nwellnhof Mar 07 '15 at 14:58
  • OK, it wouldn't be invalid as such, but it would be different. Maybe that's not a problem. On unions, C89 declares this to be "implementation-defined" and doesn't guarantee the bit pattern will actually be preserved, whereas C99/11 does (although I was misremembering this as "undefined", which is a whole different can of worms). – Alex Celeste Mar 07 '15 at 15:55
1

Try the following

n &= ~0 << 1;

The other way is

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

Take into account that converting a pointer to an object of type int is unsafe.

If you mean to set the least significant bit of the object pointed to by the pointer then the operation will look like

*next_chunk &= ~0 << 1
*next_chunk = ( *next_chunk >> 1 ) << 1;
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • That might solve the specific "bit-problem" at hand, but you are not referring to a couple of other (major) problems in OP's code. That casting from `size_t*` to `int` and back is extremely unsafe, since the size of a pointer is typically 4 or 8 bytes, and the size of an integer is typically 2 or 4 bytes (depending on your platform). By the way, to make things worse, "on the way back", OP is not even casting to `size_t*` but to `size_t`!!! – barak manos Mar 07 '15 at 12:43
  • Converting from a pointer to an int and back is safe. Use the *right* int type (i.e. `uintptr_t`) and you won't have any problem. – Alex Celeste Mar 07 '15 at 14:07
1

Are you really sure you want to unset least significant bit of address of some place in memory? I guess, you want to do that with value located at place that has this address. Maybe, you need

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

UPD: At all probabilities, pointer value (i.e. memory address) will be aligned, in other words, it has last one or two bits equal to zero.

UPD2: To do kind of aligning by yourself, you need remove asterisk at the beginning of second line

size_t * next_chunk = stack_mem.start;
next_chunk &= ~0 << 1;
George Sovetov
  • 4,942
  • 5
  • 36
  • 57
1

Setting least significant bit which starts at bit position 1 to value 0 works as follows.

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

I think the code in the snippet in question is is not used properly.

Sunil Shahu
  • 946
  • 1
  • 9
  • 24