0

I recently started C and for some reasons I couldn't get this line c |= 1 << i;

the purpose of this function I found online is to get the least significant bit from an array and then combine it, and return as a byte.

unsigned char getlsbs(unsigned char* p)
{
        int i;
        unsigned char c = 0;
        for(i = 0; i < 8; i++)
        {
                int a = p[i] & 1;
                if(a)
                {
                        c |= 1 << i;
                }
        }
        return c;
}

c |= 1 << i; would be the same as c = c | 1 << i; correct?

Could anyone explain with the example in 1s and 0s? I think it will be very helpful. Thanks!

Siv
  • 43
  • 6
  • "get the least significant bit from an array and then combine it, and return as a byte" Do you mean that you expect 8 unsigned chars, and the output is 1 unsigned char where each bit is taken from one of the input unsigned chars? As in, for {0000, 0001, 0010, 0011} you will get output 0101? – Loduwijk Nov 06 '19 at 22:19
  • @Loduwijk c would be the combination of the least significant bits from p[10], for example. Then combine it all into c, for example, c9c8c7.....c0. – Siv Nov 06 '19 at 23:31

1 Answers1

3

Well,

1<<i 

should be 1 followed by i zeros (binary)--so

1<<0 = 0001
1<<1 = 0010
1<<2 = 0100

When that is ORed with what's in C it means to force-set that bit, so:

if you take 0000 | 0010 you'll get 0010

c val| mask = result
--------------------
0010 | 0010 = 0010 as well
1111 | 0010 = 1111 (No change if the bit is already 1)
1101 | 0010 = 1111 (Just sets that one bit)
xxxx | 0010 = xx1x (All the other bits remain the same)

Finally it stores the result back into c.

So essentially it sets the ith bit in c (starting from the least significant as zero).

Further detail:

// Loop through the first 8 characters in p
for(i = 0; i < 8; i++)
{
    // Grab the least significant bit of the i'th character as an int (1 or 0)
    int a = p[i] & 1;
    // If it happens to be set (was a 1)
    if(a)
    {
        // Set the corresponding (i'th) bit in c
        c |= 1 << i;
    }
}

So if the first value of p has a lsb of 1, the lsb of c will be 1

if the second byte in p has a lsb of 1, the second bit of c will be 1

etc.

the result is that each bit of C will take the value of least significant bit of each of the first 8 bytes of P

I bet it could be coded more densely though if I really wanted to try, but this is probably targeting performance :)

Bill K
  • 62,186
  • 18
  • 105
  • 157
  • You should, even after reading the answer Bill provided, note that the line in question has poor readability since it will force people to stop and think about the order of operations. If you are maintaining this code, you would be wise to make it obvious using parenthesis. – Loduwijk Nov 06 '19 at 22:14
  • Or just call an aptly named function or macro instead... You'd think we'd be beyond dealing with stuff like this--but as with most overly dense code... it's kind of fun. – Bill K Nov 06 '19 at 22:39
  • @Loduwijk The line is completely clear to read and indeed a common ideom for bit manipulation. You always have to stop and think if you do not know; with this argument, you could also ask for parentheses with `a + b * c`. – Ctx Nov 06 '19 at 23:18
  • @BillK I still don't get it. I know how the OR works, but how does this code take the least significant bits from the array p[10] and combine it into c? Because the way I see it, it only look for the value a for the if-statement, but it doesn't actually use those value and put it into c. – Siv Nov 06 '19 at 23:22
  • @Ctx I didn't think parentheses would help either, however if you ever see someone ask--someone is wasting time that could have been better spent if the language supported more readable code. "Set Bit 3 of c" is easy to understand but not great to code--maybe setBit(3,c) would be more immediately understandable for people who hadn't seen the idiom before, and having that code in a setBit function/macro would stop you from having to guess what it was intended to do. – Bill K Nov 06 '19 at 23:25
  • @BillK Yes, of course such a macro might be a good idea. But I wouldn't consider it "bad readable code" if you decide not to. – Ctx Nov 06 '19 at 23:33
  • @BillK think you misunderstood my question. I'm asking more specifically about this line of code c |= 1 << i; I don't understand why it doesn't use the variable 'a' (which is the least significant bit we need to use to combine into variable 'c') Instead, it only shift and do the OR operation. – Siv Nov 06 '19 at 23:42
  • @Siv, `if(a)` means that the `1 << i` is only invoked at all when `a` is 1. If it's zero, we skip the whole line and don't run it for that value of `i` at all. – Charles Duffy Nov 06 '19 at 23:51
  • @CharlesDuffy Ohhhhh, so the bit we skip over will be automatically 0? – Siv Nov 07 '19 at 00:06
  • Yes, exactly; the `if(a)` causes us to skip over bits that are 0. – Charles Duffy Nov 07 '19 at 00:08
  • @Siv yeah, that's why c is initialized to 0. – Bill K Nov 07 '19 at 00:13
  • ...well, that would need to be the case regardless, as `c |= 0 << i` does nothing at all regardless of what the initial bit in `c` is. – Charles Duffy Nov 07 '19 at 00:50
  • @Ctx My wording "poor readability" was a bit much, but it is not "completely clear to read" either. Just because you can read it easily does not make something "completely clear". I have seen many times bit manipulations are being done and people make operation order mistakes. Plenty of times I've seen people stop and ask about order. No, it's not the same as "a + b * c" because they teach people that in 3rd grade and pound it into people for years; that is trivial (though parenthesis still wouldn't hurt). Ops |&>><< order are non-trivial and people forget them frequently. – Loduwijk Nov 07 '19 at 16:40
  • @BillK I kept thinking in bits. I wasn't thinking too much about the part of c being initialized to 0. It all makes sense now. Thank you so much. – Siv Nov 07 '19 at 17:07