2

Doing some experiences to understand bit fields

Hi have this code:

01  #include <stdio.h>
02  #include <stdlib.h>
03
04  void AddToBitfield(int *bitfield, int bitCount, int value);
05  int ReadFromBitfield(int *bitfield, int bitCount);
06
07  int main(){
08      /*Device list (0 Modem,1 Keyboard,2 Mouse,3 Speakers,4 Joystick,5 Flash Drive,6 Scanner,7 Printer,8 Microphone,9 Webcam,10 Monitor)*/
09      int device=0,memLoc=0,data=0;
10      int number = 0;
11
12      memLoc = 01; /*put 01 or 10*/
13      device = 15; /*Device id*/
14      data = 12343; /*Data to store - Only less or equal then 65535*/
15
16      AddToBitfield(&number,4,device);
17      AddToBitfield(&number,16,data);
18      AddToBitfield(&number,2,memLoc);
19
20      printf("--%d---\n",number);
21
22      printf("Memory location: %d\n",ReadFromBitfield(&number,2));
23      printf("Data stored: %d\n",ReadFromBitfield(&number,16));
24      printf("Device: %d\n",ReadFromBitfield(&number,4));
25
26      return 0;
27  }
28
29  void AddToBitfield(int *bitfield, int bitCount, int value){
30      *bitfield <<= bitCount;
31      *bitfield |= value;
32  }
33
34  int ReadFromBitfield(int *bitfield, int bitCount){
35      int value = *bitfield & ((1 << bitCount) - 1);
36      *bitfield >>= bitCount;
37
38      return value;
39  }

Using data = 12343; and with memLoc = 01; or memLoc = 10; the printf will show all as expected.

Using data = 12346; and with memLoc = 01; or memLoc = 10; its the same thing. The printf will show all as expected.

But if I use data = 12344; or data = 12345; if I use memLoc = 01; the print will show what is expected in both cases, but if i use memLoc = 10; on the first case it will print Data stored: 12346 and on the second case Data stored: 12347.

Why is this happening?

Favolas
  • 6,963
  • 29
  • 75
  • 127
  • 4
    You reserve only 2 bits for "memLoc". So it can only store values 0 through 3. Yet you stuff 10 into it. – Hans Passant Apr 22 '12 at 17:28
  • 3
    Also use `unsigned int` for bit fields, since left bitshifts are undefined when the left operand is negative (relevant [question](http://stackoverflow.com/q/3784996/1250595)). Also avoid preceding numbers with a 0, since C will then interpret the number as an octal number; e.g. 010 = 8. – Anthales Apr 22 '12 at 17:38
  • @HansPassant Thanks. Was thinking in binary. Should be 2 – Favolas Apr 22 '12 at 18:22
  • if you want it binary try prefix with 0b: `0b010 == 2` – moooeeeep Apr 22 '12 at 18:35
  • @moooeeeep There is no binary prefix for number constants in C and C++; although one can use [boost](http://www.boost.org/doc/libs/1_42_0/libs/utility/utility.htm#BOOST_BINARY) for this. – Anthales Apr 22 '12 at 18:40
  • @Anthales , It appears to work though (gcc): http://ideone.com/yJc9i – moooeeeep Apr 22 '12 at 18:42
  • @moooeeeep It's a non-standard [GNU extension](http://gcc.gnu.org/onlinedocs/gcc/Binary-constants.html) which GCC uses. Also don't trust online IDEs, they are known to fail on some really trivial sample codes. – Anthales Apr 22 '12 at 18:44
  • @Favolas: I'll add one more comment: Bit fields are hard to get right in C, as you may have noticed, reading the comments on k06as answer. It gets even worse if you want to de/serialize them (e.g. send them over network to another computer and receive them), as the byte and bit orders can differ on different platforms. Most of the times it's best to avoid them like the plague; or, if really needed, search for libraries, which already solved (or tried to solve) all these problems. – Anthales Apr 22 '12 at 19:35
  • Hi everybody. Thanks for this great "discussion". It was usefull to my learning process – Favolas Apr 23 '12 at 08:17

1 Answers1

2

You can use this C struct for reading/writing bit fields:

struct MyType
{
    uint16_t memLoc:4;
    uint16_t device:4;
    uint16_t _reserved:8;
    uint16_t data;
};

Your code will looks like this:

int main()
{
    // ...

    struct MyType number;
    uint32_t numberInt;

    number.memLoc = 1;
    number.device = 15;
    number.data = 12343;

    memcpy(&numberInt, &number, sizeof(struct MyType));
    printf("--%d---\n", numberInt);

    printf("Memory location: %d\n", number.memLoc);
    printf("Data stored: %d\n", number.data);
    printf("Device: %d\n", number.device);

    // ...
}

Tell me how many bits are in each field, and i'll fix that structure for you.

Here is an article about bit fields: http://en.wikipedia.org/wiki/Bit_field

k06a
  • 17,755
  • 10
  • 70
  • 110
  • 1
    The line `int numberInt = *(int*)&number;` (`int &` should be `int`) isn't good practice, because is for one assumes `int` to be exactly `2*sizeof(short)` bit, `short` to be 16 bit and `&number` to be correctly aligned. A bit better (no pun intended) would be to write: `uint32_t numberInt; memcpy(&numberInt, &number)` and use `uint16_t` instead of `short`; although I'm not sure if there couldn't be any padding in the struct before the `data` member.. – Anthales Apr 22 '12 at 18:25
  • It still has the pointer alignment problem (which `memcpy` works around), but on the other hand this probably won't matter much, as OP is only trying to understand bitfields, not trying to write the most portable bitfield library (MPBFL™). – Anthales Apr 22 '12 at 18:56
  • 1
    `MyType` needs to be preceded with `struct` unless you've `typedef`ed it. Otherwise it´s not C but C++. – onemasse Apr 22 '12 at 18:59
  • I should read `memcpy(&numberInt, &number, sizeof(struct MyType))` - I wanted to fix my comment, but couldn't, so I didn't bother anymore ;) – Anthales Apr 22 '12 at 19:09
  • I think one still does to set the `_reserved` member to 0... Man, I should've written my own answer; would have saved a lot of time on commenting :) – Anthales Apr 22 '12 at 19:23
  • @k06a Thanks for your effort but as Anthales said, was just trying to learn the basics. – Favolas Apr 23 '12 at 08:18