Very simple C question

Discussion in 'Programmer's Corner' started by wannaBinventor, Oct 1, 2010.

  1. wannaBinventor

    Thread Starter Member

    Apr 8, 2010
    179
    4
    I've got a book on order to learn about C, but I was hoping you guys could help me in the mean time.

    I'm looking at microchipc.com FAQs for some help, and I came across this which I don't understand.

    This supposedly turns on and off one bit at a time:

    unsigned char x=0b0001;
    bit_set(x,3); //now x=0b1001;
    bit_clr(x,0); //now x=0b1000;*/
    #define bit_set(var,bitno) ((var) |= 1 << (bitno))
    #define bit_clr(var,bitno) ((var) &= ~(1 << (bitno)))

    This apparently turns on and off multiple bits:

    unsigned char x=0b1010;
    bits_on(x,0b0001); //now x=0b1011
    bits_off(x,0b0011); //now x=0b1000 */
    #define bits_on(var,mask) var |= mask
    #define bits_off(var,mask) var &= ~0 ^ mask

    Can some one please break it down Barney style for me? Some of the operators are throwing me off. Furthermore, I don't get why it's defined AFTER the command. I don't really understand anything about C, but I thought definitions had to be made before the commands.
     
  2. sceadwian

    New Member

    Jun 1, 2009
    499
    37
    In case you can't tell from the length of my following post never say 'simple question' about anything if you don't understand it =) it's almost never as simple as you think it is.

    The placements of the definitions is definitely not typical, depending on the compiler though it may not be a problem, #defines are generally made at the top of the header file or before any other function but I don't think it's required (at least not by all compilers) The # means it's a compiler directive which means it doesn't actually have anything whatsoever to do with the code itself, it's simply an insertion of an instruction that explains something to the compiler about the code, in this case it makes those masking bitwise operations a lot more easy for plain language C users to understand. Everyone knows what bits_on and bit_set should do intuativly, the Boolean logic to translate and let the compiler know what we want it to do is contained in the #defines. Defines are NOT functions, although they're called like one in this example and look just like many other C function calls they really act more like assembly language macros.

    Ahh the wonderful world of bitmasking! You will find this kind of thing used a LOT in micro controller C because it's as close as you can get to programming in assembly while still writing it in C, those binary operators execute very fast. Personally I don't intuitively know how to read this kind of code I still have to take it apart bit by bit to figure out how it's all working but with a good intuitive understanding of how bit manipulation works it's MUCH easier to read.

    I think http://en.wikipedia.org/wiki/Bitwise_operation would be a very good read for you as it will help you bitwise operations, study it and I'd recommend startin a new thread for questions that you may have because it's not directly related to C, just happens to be common for micro controller C code, you'll almost never see that kind of bit manipulation in 'real' C. I'd love to take part in such a thread because personally my bit manipulation is a little rough and good knowledge of it can help you right VERY fast code. Again reading a book on C will not teach you to write code for a micro controller based C it will teach you basic C syntax that is all, everything else it teaches you is going to be applicable to the code and system that it was designed to teach on which can be quiet varied but are almost always machines that are far above a micro controller.
     
    Last edited: Oct 1, 2010
  3. Markd77

    Senior Member

    Sep 7, 2009
    2,803
    594
    This is probably a good cheat sheet too.
    http://en.wikipedia.org/wiki/C_operators
    C scares me.
    Once these are defined, they are as easy to use as assembler and it doesn't really matter if you understand them.
    I think this translates to
    #define bit_clr(var,bitno) ((var) &= ~(1 << (bitno)))

    var AND NOT(1 leftshifted bitnumber of times)

    so if bitnumber was 3
    you would get
    var AND NOT(00001000)
    =
    var AND 11110111
    which clears bit 3


     
  4. DonQ

    Active Member

    May 6, 2009
    320
    11
    in:

    #define bit_set(var,bitno) ((var) |= 1 << (bitno))

    (var) |= 1 << (bitno)

    is the same effect as

    (var) = (var) | (1 << (bitno))

    It's just an abbreviated way of saying it. It also is a hint to the compiler that once it has var in a register, it should keep it there if it can because it will be needing to use it a second time.

    the (1 << bitno) part just returns a value with one bit set. It does this by starting with a 1 in the LSB position, and shifts it left bitno times. (With a constant, this is done by the preprocessor during the compile, not requiring any instructions at run-time.)

    The | does a bit-by-bit OR. The |= does it to var, then immediately stores the result in var.


    #define bit_clr(var,bitno) ((var) &= ~(1 << (bitno)))

    This is the opposite effect. When the (1<<(bitno)) creates the value with one bit set, ~ does a bit-by-bit complement, switching 1s to 0s and vice versa leaving you with a value with all 1's except for a single zero. Then it ANDs the value, clearing the single bit, and storing it back in var.

    The 'mask' versions just rely on the user to create a value with the bits matching the bits to set or clear. You could actually use these like:

    #define bits_on(var,mask) var |= mask

    bits_on(my_byte, 1<<3); // to set the third bit of my_byte, same as bit_set.

    I'd be a little careful of things like:

    #define bits_off(var,mask) var &= ~0 ^ mask

    This is using an exclusive-or to flip the bits, but I don't remember the operator precedence between ~ and ^ and & (meaning which one happens first?) It is always a good practice, especially in macros to use extra parenthesis to make things crystal clear.

    example:

    bits_off(my_byte, 4 | 8);

    becomes

    var &= ~0 ^ 4 | 8;

    which will probably not do what you were thinking when you invoked the macro.

    Change it (at least) to:

    #define bits_off(var,mask) var &= ~0 ^ (mask)

    and even though it's not really needed, it's just good practice to change

    #define bits_on(var,mask) var |= mask
    #define bits_on(var,mask) var |= (mask)

    var doesn't need parenthesis because it must be an Lvalue (next topic).

    So there! That's the short version :D
     
  5. romel_emperado

    New Member

    Jul 23, 2009
    5
    0
    its not simple buddy..hehe i cant understand that also.. hehe
     
  6. t06afre

    AAC Fanatic!

    May 11, 2009
    5,939
    1,222
    Code ( (Unknown Language)):
    1.  
    2. #define bitset(var, bitno) ((var) |= 1UL << (bitno))
    3. #define bitclr(var, bitno) ((var) &= ~(1UL << (bitno)))
    4. #define toggle_bit(var,bitno) (var ^= (1UL << bitno))
    5. #define test_bit(var,bitno) (var & (1UL << bitno))
    6.  
    This a colloection of common macros used in C. If the compoler is smart it will recognize what the user want and optimize using proper machine code. Hence for a PIC MCU the compiler will use the BSF machine code instruction instead of in a literal sense implement
    Code ( (Unknown Language)):
    1. var=var | 1UL << (bitno)
    If you work with PIC MCUs and LCDs in 4 bit mode. This one might come handy. Using the SWAPF instruction.
    Code ( (Unknown Language)):
    1.  
    2. var = (var >> 4) | (var << 4); because it will flipp the high and low nibble
    3.  
    If you want to set or clear several bits in a integer use bitwise OR, or AND. If you want to flip bits use XOR. More here http://www.somacon.com/p125.php
    I dislike the macro format. So I often use a union like this
    Code ( (Unknown Language)):
    1. union {
    2. char unsigned data;
    3. struct {
    4. unsigned DISP_EN:1;
    5. unsigned DISP_RS:1;
    6. unsigned DISP_RW:1;
    7. };
    8. }LCD;
    9. LCD.DISP_EN=1;
    10. LCD.DISP_RW=1;
    11. LCD.DISP_RS=1;
    12.  
    the LCD.DISP_EN will be bit 0 in the variable data, and LCD.DISP_RW will be bit no 1, and so on
     
    Last edited: Oct 3, 2010
  7. wannaBinventor

    Thread Starter Member

    Apr 8, 2010
    179
    4
    Thanks for all the help guys. This helps clarify a lot.
     
  8. DonQ

    Active Member

    May 6, 2009
    320
    11
    Code ( (Unknown Language)):
    1.  
    2. var = (var >> 4) | (var << 4); because it will flipp the high and low nibble
    3.  
    Careful, many compilers (all that I work with) will 'sign extend' for right shifts if var is a signed variable. In other words, if the number was minus before the shift, it will be minus after the shift.

    Like this:

    Code ( (Unknown Language)):
    1. signed char var = 0x80;
    2.  
    3. (var >> 4) // code fragment
    4.  
    5. 10000000 // at start   = -128 signed value
    6. 11000000 // after first shift = -64
    7. 11100000 // after second shift = -32
    8. 11110000 // after third shift = -16
    9. 11111000 // after the last shift = -8
    No matter what you OR now with the high nibble, the upper bits will all be 1's. Not what you want.

    To make the macro safe, it needs to have a typecast (or some other safety net).

    Code ( (Unknown Language)):
    1.  
    2. var = (((unsigned char) var) >> 4) | (var << 4);
    3.  
    This should work on all compilers, but is still limited to 8-bits vars only.
     
  9. t06afre

    AAC Fanatic!

    May 11, 2009
    5,939
    1,222
    It works great in HI-tech C for Microchip MCU. The compiler know this means "use the SWAPF machine code instruction" The SWAPF is an instruction for swapping nibbles inside a byte. No actual shifting is done. For others compilers I do not know. The OP do not use the HI-Tech C. It could very well be that his/hers C version do not support this.
    By the way the only time I have used this has been with LCDs in 4 nit mode. Can not see much use of it elsewhere
     
    Last edited: Oct 3, 2010
Loading...